support for poetry and epigraphs
This commit is contained in:
1 parent
316b10d015
commit
de04b9cbed
11 files changed
+787
-10
No files matched your search
@@ -15,6 +15,7 @@ data class Fb2Book(
|
||||
val documentInfo: Fb2DocumentInfo = Fb2DocumentInfo(),
|
||||
val bodyTitle: List<String> = emptyList(),
|
||||
val bodyImages: List<Fb2ImageRef> = emptyList(),
|
||||
val bodyEpigraphs: List<Fb2Epigraph> = emptyList(),
|
||||
val sections: List<Fb2Section> = emptyList(),
|
||||
val binaries: List<Fb2Binary> = emptyList(),
|
||||
) {
|
||||
@@ -69,10 +70,50 @@ data class Fb2Binary(
|
||||
sealed interface Fb2Block {
|
||||
data class Paragraph(val content: Fb2Text) : Fb2Block
|
||||
data class Subtitle(val content: Fb2Text) : Fb2Block
|
||||
data class Epigraph(val epigraph: Fb2Epigraph) : Fb2Block
|
||||
data class Cite(val cite: Fb2Cite) : Fb2Block
|
||||
data class Poem(val poem: Fb2Poem) : Fb2Block
|
||||
data class Image(val image: Fb2ImageRef) : Fb2Block
|
||||
data object EmptyLine : Fb2Block
|
||||
}
|
||||
|
||||
data class Fb2Epigraph(
|
||||
val blocks: List<Fb2EpigraphBlock> = emptyList(),
|
||||
val textAuthors: List<Fb2Text> = emptyList(),
|
||||
)
|
||||
|
||||
data class Fb2Cite(
|
||||
val blocks: List<Fb2EpigraphBlock> = emptyList(),
|
||||
val textAuthors: List<Fb2Text> = emptyList(),
|
||||
)
|
||||
|
||||
sealed interface Fb2EpigraphBlock {
|
||||
data class Paragraph(val content: Fb2Text) : Fb2EpigraphBlock
|
||||
data class Subtitle(val content: Fb2Text) : Fb2EpigraphBlock
|
||||
data class Poem(val poem: Fb2Poem) : Fb2EpigraphBlock
|
||||
data class Cite(val cite: Fb2Cite) : Fb2EpigraphBlock
|
||||
data object EmptyLine : Fb2EpigraphBlock
|
||||
}
|
||||
|
||||
data class Fb2Poem(
|
||||
val title: List<Fb2Text> = emptyList(),
|
||||
val epigraphs: List<Fb2Epigraph> = emptyList(),
|
||||
val blocks: List<Fb2PoemBlock> = emptyList(),
|
||||
val textAuthors: List<Fb2Text> = emptyList(),
|
||||
val date: String? = null,
|
||||
)
|
||||
|
||||
sealed interface Fb2PoemBlock {
|
||||
data class Subtitle(val content: Fb2Text) : Fb2PoemBlock
|
||||
data class Stanza(val stanza: Fb2Stanza) : Fb2PoemBlock
|
||||
}
|
||||
|
||||
data class Fb2Stanza(
|
||||
val title: List<Fb2Text> = emptyList(),
|
||||
val subtitle: Fb2Text? = null,
|
||||
val verses: List<Fb2Text> = emptyList(),
|
||||
)
|
||||
|
||||
data class Fb2Text(
|
||||
val spans: List<Fb2TextSpan>,
|
||||
) {
|
||||
|
||||
@@ -32,6 +32,7 @@ internal object Fb2XmlMapper {
|
||||
documentInfo = documentInfoFrom(documentInfo),
|
||||
bodyTitle = body?.first("title")?.children("p")?.mapNotNull { it.text().ifBlank { null } }.orEmpty(),
|
||||
bodyImages = body?.children("image")?.mapNotNull(::imageRefFrom).orEmpty(),
|
||||
bodyEpigraphs = body?.children("epigraph")?.map(::epigraphFrom).orEmpty(),
|
||||
sections = body?.children("section")?.map(::sectionFrom).orEmpty(),
|
||||
binaries = root.children("binary").mapNotNull(::binaryFrom),
|
||||
)
|
||||
@@ -124,6 +125,9 @@ internal object Fb2XmlMapper {
|
||||
when (child.localName) {
|
||||
"p" -> Fb2Block.Paragraph(textFrom(child))
|
||||
"subtitle" -> Fb2Block.Subtitle(textFrom(child))
|
||||
"epigraph" -> Fb2Block.Epigraph(epigraphFrom(child))
|
||||
"cite" -> Fb2Block.Cite(citeFrom(child))
|
||||
"poem" -> Fb2Block.Poem(poemFrom(child))
|
||||
"image" -> imageRefFrom(child)?.let(Fb2Block::Image)
|
||||
"empty-line" -> Fb2Block.EmptyLine
|
||||
else -> null
|
||||
@@ -140,8 +144,64 @@ internal object Fb2XmlMapper {
|
||||
)
|
||||
}
|
||||
|
||||
private fun poemFrom(element: XmlElement): Fb2Poem =
|
||||
Fb2Poem(
|
||||
title = titleFrom(element.first("title")),
|
||||
epigraphs = element.children("epigraph").map(::epigraphFrom),
|
||||
blocks = element.nodes.mapNotNull { node ->
|
||||
val child = (node as? XmlNode.ElementNode)?.element ?: return@mapNotNull null
|
||||
when (child.localName) {
|
||||
"subtitle" -> Fb2PoemBlock.Subtitle(textFrom(child))
|
||||
"stanza" -> Fb2PoemBlock.Stanza(stanzaFrom(child))
|
||||
else -> null
|
||||
}
|
||||
},
|
||||
textAuthors = element.children("text-author").map(::textFrom),
|
||||
date = element.first("date")?.text()?.ifBlank { null },
|
||||
)
|
||||
|
||||
private fun epigraphFrom(element: XmlElement): Fb2Epigraph =
|
||||
Fb2Epigraph(
|
||||
blocks = element.nodes.mapNotNull { node ->
|
||||
val child = (node as? XmlNode.ElementNode)?.element ?: return@mapNotNull null
|
||||
when (child.localName) {
|
||||
"p" -> Fb2EpigraphBlock.Paragraph(textFrom(child))
|
||||
"poem" -> Fb2EpigraphBlock.Poem(poemFrom(child))
|
||||
"cite" -> Fb2EpigraphBlock.Cite(citeFrom(child))
|
||||
"empty-line" -> Fb2EpigraphBlock.EmptyLine
|
||||
else -> null
|
||||
}
|
||||
},
|
||||
textAuthors = element.children("text-author").map(::textFrom),
|
||||
)
|
||||
|
||||
private fun citeFrom(element: XmlElement): Fb2Cite =
|
||||
Fb2Cite(
|
||||
blocks = element.nodes.mapNotNull { node ->
|
||||
val child = (node as? XmlNode.ElementNode)?.element ?: return@mapNotNull null
|
||||
when (child.localName) {
|
||||
"p" -> Fb2EpigraphBlock.Paragraph(textFrom(child))
|
||||
"subtitle" -> Fb2EpigraphBlock.Subtitle(textFrom(child))
|
||||
"poem" -> Fb2EpigraphBlock.Poem(poemFrom(child))
|
||||
"empty-line" -> Fb2EpigraphBlock.EmptyLine
|
||||
else -> null
|
||||
}
|
||||
},
|
||||
textAuthors = element.children("text-author").map(::textFrom),
|
||||
)
|
||||
|
||||
private fun stanzaFrom(element: XmlElement): Fb2Stanza =
|
||||
Fb2Stanza(
|
||||
title = titleFrom(element.first("title")),
|
||||
subtitle = element.first("subtitle")?.let(::textFrom),
|
||||
verses = element.children("v").map(::textFrom),
|
||||
)
|
||||
|
||||
private fun titleFrom(element: XmlElement?): List<Fb2Text> =
|
||||
element?.children("p")?.map(::textFrom).orEmpty()
|
||||
|
||||
private fun textFrom(element: XmlElement): Fb2Text =
|
||||
Fb2Text(spansFrom(element.nodes).mergeAdjacent())
|
||||
Fb2Text(spansFrom(element.nodes).mergeAdjacent().trimBoundaryWhitespace())
|
||||
|
||||
private fun spansFrom(nodes: List<XmlNode>, styles: Set<Fb2TextStyle> = emptySet()): List<Fb2TextSpan> =
|
||||
nodes.flatMap { node ->
|
||||
@@ -179,6 +239,14 @@ internal object Fb2XmlMapper {
|
||||
return merged
|
||||
}
|
||||
|
||||
private fun List<Fb2TextSpan>.trimBoundaryWhitespace(): List<Fb2TextSpan> {
|
||||
if (isEmpty()) return emptyList()
|
||||
val trimmed = toMutableList()
|
||||
trimmed[0] = trimmed[0].copy(text = trimmed[0].text.trimStart())
|
||||
trimmed[trimmed.lastIndex] = trimmed.last().copy(text = trimmed.last().text.trimEnd())
|
||||
return trimmed.filter { it.text.isNotEmpty() }
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendSection(section: Fb2Section) {
|
||||
append("<section>")
|
||||
section.title?.takeIf { it.isNotBlank() }?.let {
|
||||
|
||||
Reference in new issue
Block a user