import Foundation extension RDEPUBReaderController { func resolvedCurrentTableOfContentsItem() -> RDEPUBReaderTableOfContentsItem? { let items = flattenedTableOfContentsItems( from: publication?.tableOfContents ?? [], includePageNumbers: false ) guard !items.isEmpty else { return nil } guard let publication, let currentLocation = currentVisibleLocation(), let normalizedCurrentHref = publication.resourceResolver.normalizedHref(currentLocation.href) else { return nil } if let textBook, let chapterData = textBook.chapterData( for: currentLocation, resolver: publication.resourceResolver, bookIdentifier: currentBookIdentifier ), let tocItem = chapterData.primaryTableOfContentsItem( from: publication.tableOfContents, normalizer: { publication.resourceResolver.normalizedHref($0) } ) { return items.last { $0.href == tocItem.href } ?? items.last { guard let normalizedItemHref = publication.resourceResolver.normalizedHref($0.href.components(separatedBy: "#").first ?? $0.href) else { return false } return normalizedItemHref == normalizedCurrentHref } } return items.last { item in guard let normalizedItemHref = publication.resourceResolver.normalizedHref(item.href.components(separatedBy: "#").first ?? item.href) else { return false } return normalizedItemHref == normalizedCurrentHref } } func flattenedTableOfContentsItems( from items: [EPUBTableOfContentsItem], depth: Int = 0, includePageNumbers: Bool = true ) -> [RDEPUBReaderTableOfContentsItem] { items.flatMap { item in let location = RDEPUBLocation(bookIdentifier: currentBookIdentifier, href: item.href, progression: 0) let pageNumber = includePageNumbers ? resolvedTableOfContentsPageNumber(for: location) : nil let isReadable = isReadableTableOfContentsLocation(location) let current = RDEPUBReaderTableOfContentsItem( title: item.title, href: item.href, depth: depth, pageNumber: pageNumber, isReadable: isReadable ) return [current] + flattenedTableOfContentsItems( from: item.children, depth: depth + 1, includePageNumbers: includePageNumbers ) } } private func resolvedTableOfContentsPageNumber(for location: RDEPUBLocation) -> Int? { if let publication, let bookPageMap = readerContext.bookPageMap, let spineIndex = readerContext.normalizedSpineIndex(for: location), let entry = bookPageMap.entry(forSpineIndex: spineIndex) { let normalizedLocation = publication.resourceResolver.normalizedLocation( location, bookIdentifier: currentBookIdentifier ) ?? location let localPageIndex: Int if let summary = readerContext.chapterSummary(forSpineIndex: spineIndex) { let offset = chapterOffset(for: normalizedLocation, fallbackEntry: entry) localPageIndex = summary.pageRanges.firstIndex { let range = $0.nsRange return offset >= range.location && offset <= max(range.location + range.length - 1, range.location) } ?? fallbackLocalPageIndex(for: normalizedLocation, pageCount: entry.pageCount) } else { localPageIndex = fallbackLocalPageIndex(for: normalizedLocation, pageCount: entry.pageCount) } return bookPageMap.absolutePageIndex( spineIndex: spineIndex, localPageIndex: min(max(localPageIndex, 0), max(entry.pageCount - 1, 0)) ).map { $0 + 1 } } if let textBook, let publication, let chapterData = textBook.chapterData(for: location, resolver: publication.resourceResolver, bookIdentifier: currentBookIdentifier) { let normalizedLocation = publication.resourceResolver.normalizedLocation( location, bookIdentifier: currentBookIdentifier ) ?? location return chapterData.pageNumber(for: normalizedLocation) ?? textBook.pageNumber( for: location, resolver: publication.resourceResolver, bookIdentifier: currentBookIdentifier ) } return readingSession?.pageIndex(for: location, bookIdentifier: currentBookIdentifier).map { $0 + 1 } } }