Skip to content

Commit

Permalink
Improve title handling again. (Ticket: #PP-1954)
Browse files Browse the repository at this point in the history
  • Loading branch information
io7m committed Nov 26, 2024
1 parent ae8b236 commit d4f5e95
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 25 deletions.
11 changes: 10 additions & 1 deletion README-CHANGES.xml
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@
</c:change>
</c:changes>
</c:release>
<c:release date="2024-11-26T13:59:24+00:00" is-open="false" ticket-system="org.lyrasis.jira" version="6.6.0">
<c:release date="2024-11-26T00:00:00+00:00" is-open="false" ticket-system="org.lyrasis.jira" version="6.6.0">
<c:changes>
<c:change date="2024-11-26T00:00:00+00:00" summary="Do not miss non-leaf titles.">
<c:tickets>
Expand All @@ -180,6 +180,15 @@
</c:change>
</c:changes>
</c:release>
<c:release date="2024-11-26T18:06:43+00:00" is-open="true" ticket-system="org.lyrasis.jira" version="6.7.0">
<c:changes>
<c:change date="2024-11-26T00:00:00+00:00" summary="Improve title handling again.">
<c:tickets>
<c:ticket id="PP-1954"/>
</c:tickets>
</c:change>
</c:changes>
</c:release>
</c:releases>
<c:ticket-systems>
<c:ticket-system default="true" id="org.lyrasis.jira" url="https://ebce-lyrasis.atlassian.net/browse/"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,32 @@ import org.librarysimplified.r2.vanilla.internal.SR2NavigationNode.SR2Navigation
import org.librarysimplified.r2.vanilla.internal.SR2NavigationNode.SR2NavigationResourceNode
import org.readium.r2.shared.publication.Link
import org.readium.r2.shared.publication.Publication
import org.slf4j.LoggerFactory
import java.net.URI

object SR2NavigationGraphs {

private val logger =
LoggerFactory.getLogger(SR2NavigationGraphs::class.java)

private data class FlatTOCLink(
val depth: Int,
val link: Link,
)

fun create(
publication: Publication,
): SR2NavigationGraph {
val flatToc = mutableListOf<FlatTOCLink>()
flattenTOC(flatToc, publication.tableOfContents, depth = 0)

val readingOrder =
publication.readingOrder.mapIndexed { index, link ->
this.makeReadingOrderNode(publication, index, link)
this.makeReadingOrderNode(flatToc, index, link)
}
val resources =
publication.resources.map { link ->
this.makeResourceNode(publication, link)
this.makeResourceNode(flatToc, link)
}

return SR2NavigationGraph(
Expand All @@ -26,31 +39,48 @@ object SR2NavigationGraphs {
)
}

private fun flattenTOC(
flatToc: MutableList<FlatTOCLink>,
tableOfContents: List<Link>,
depth: Int,
) {
for (entry in tableOfContents) {
flatToc.add(FlatTOCLink(depth, entry))
flattenTOC(
flatToc = flatToc,
tableOfContents = entry.children,
depth = depth + 1,
)
}
}

private fun makeResourceNode(
publication: Publication,
flatTOC: List<FlatTOCLink>,
link: Link,
): SR2NavigationResourceNode {
return SR2NavigationResourceNode(
navigationPoint = this.makeNavigationPoint(publication, link),
navigationPoint = this.makeNavigationPoint(flatTOC, link),
)
}

private fun makeReadingOrderNode(
publication: Publication,
flatTOC: List<FlatTOCLink>,
index: Int,
link: Link,
): SR2NavigationReadingOrderNode {
return SR2NavigationReadingOrderNode(
navigationPoint = this.makeNavigationPoint(publication, link),
navigationPoint = this.makeNavigationPoint(flatTOC, link),
index = index,
)
}

private fun makeNavigationPoint(publication: Publication, link: Link): SR2NavigationPoint {
val title =
link.title?.takeIf(String::isNotBlank)
?: titleFromTOC(null, publication.tableOfContents, link)
?: ""
private fun makeNavigationPoint(
flatTOC: List<FlatTOCLink>,
link: Link,
): SR2NavigationPoint {
val title = this.findTitleFor(flatTOC, link)
this.logger.debug("Title: {} -> {}", link.href, title)

return SR2NavigationPoint(
title,
SR2Locator.SR2LocatorPercent.create(link.href, 0.0),
Expand All @@ -59,25 +89,69 @@ object SR2NavigationGraphs {

/**
* Perform a depth-first search for a title in the toc tree.
* In case of multiples matches, deeper items have precedence because they're likely to be more specific.
* In case of multiples matches, deeper items have precedence because they're likely to be
* more specific.
*/

private fun titleFromTOC(tocEntryParent: Link?, toc: List<Link>, link: Link): String? {
for (entry in toc) {
this.titleFromTOC(entry, entry.children, link)?.let {
return it
}
private fun findTitleFor(
flatTOC: List<FlatTOCLink>,
link: Link,
): String {
val linkTitle = link.title?.trim()
if (!linkTitle.isNullOrBlank()) {
return linkTitle
}

for (entry in toc) {
if (entry.href == link.href && entry.title != null) {
return entry.title
}
val bestLink =
flatTOC.filter { flatLink -> hasSuitableTitle(candidateEntry = flatLink.link, link) }
.sortedBy(FlatTOCLink::depth)
.lastOrNull()

return bestLink?.link?.title ?: ""
}

private fun hasSuitableTitle(
candidateEntry: Link,
link: Link,
): Boolean {
/*
* If there isn't a non-blank title, then we know this entry isn't suitable.
*/

val title = candidateEntry.title ?: return false
if (title.isBlank()) {
return false
}

/*
* If the URLs match exactly, then we know this entry is suitable.
*/

if (candidateEntry.href == link.href) {
return true
}

if (tocEntryParent?.href == link.href && tocEntryParent.title != null) {
return tocEntryParent.title
/*
* Otherwise, if we _don't_ have a fragment, and the target link body matches ours but
* _does_ have a fragment, we treat it as suitable.
*/

try {
val linkAsURI = URI.create(link.href.toString())
if (!linkAsURI.fragment.isNullOrBlank()) {
return false
}

val linkWithoutFragment =
URI.create(linkAsURI.toString().substringBefore('#'))
val candidateEntryAsURI =
URI.create(candidateEntry.href.toString())
val candidateEntryWithoutFragment =
URI.create(candidateEntryAsURI.toString().substringBefore('#'))

return candidateEntryWithoutFragment == linkWithoutFragment
} catch (e: Exception) {
return false
}
return null
}
}

0 comments on commit d4f5e95

Please sign in to comment.