- 实现EPUB阅读器搜索功能及选中注释功能 - 优化CFI模块,修复代码审查发现的11个问题 - 实现大书远距目录跳转与后台补全优化方案 - 优化设置面板与章节运行时联动 - 重构及大量改进优化
113 lines
3.5 KiB
Swift
113 lines
3.5 KiB
Swift
import Foundation
|
|
|
|
struct RDEPUBBookPageMapEntry {
|
|
|
|
let spineIndex: Int
|
|
|
|
let href: String
|
|
|
|
let title: String
|
|
|
|
let pageCount: Int
|
|
|
|
let absolutePageStart: Int
|
|
|
|
let fragmentOffsets: [String: Int]
|
|
}
|
|
|
|
struct RDEPUBBookPageMap {
|
|
|
|
let entries: [RDEPUBBookPageMapEntry]
|
|
|
|
private let indexBySpine: [Int: Int]
|
|
|
|
let totalPages: Int
|
|
|
|
init(entries: [RDEPUBBookPageMapEntry]) {
|
|
self.entries = entries
|
|
var mapping: [Int: Int] = [:]
|
|
for (i, entry) in entries.enumerated() {
|
|
mapping[entry.spineIndex] = i
|
|
}
|
|
self.indexBySpine = mapping
|
|
self.totalPages = entries.last.map { $0.absolutePageStart + $0.pageCount } ?? 0
|
|
}
|
|
|
|
static let empty = RDEPUBBookPageMap(entries: [])
|
|
|
|
func absolutePageIndex(spineIndex: Int, localPageIndex: Int) -> Int? {
|
|
guard let idx = indexBySpine[spineIndex] else { return nil }
|
|
let entry = entries[idx]
|
|
guard localPageIndex >= 0, localPageIndex < entry.pageCount else { return nil }
|
|
return entry.absolutePageStart + localPageIndex
|
|
}
|
|
|
|
func spineIndex(forAbsolutePage absolutePage: Int) -> Int? {
|
|
guard absolutePage >= 0, absolutePage < totalPages else { return nil }
|
|
|
|
var lo = 0, hi = entries.count
|
|
while lo < hi {
|
|
let mid = lo + (hi - lo) / 2
|
|
if entries[mid].absolutePageStart <= absolutePage {
|
|
lo = mid + 1
|
|
} else {
|
|
hi = mid
|
|
}
|
|
}
|
|
guard lo > 0 else { return nil }
|
|
return entries[lo - 1].spineIndex
|
|
}
|
|
|
|
func localPageIndex(forAbsolutePage absolutePage: Int) -> Int? {
|
|
guard let si = spineIndex(forAbsolutePage: absolutePage),
|
|
let idx = indexBySpine[si] else { return nil }
|
|
let entry = entries[idx]
|
|
let local = absolutePage - entry.absolutePageStart
|
|
guard local >= 0, local < entry.pageCount else { return nil }
|
|
return local
|
|
}
|
|
|
|
func entry(forSpineIndex spineIndex: Int) -> RDEPUBBookPageMapEntry? {
|
|
guard let idx = indexBySpine[spineIndex] else { return nil }
|
|
return entries[idx]
|
|
}
|
|
|
|
func chapterIndex(forSpineIndex spineIndex: Int) -> Int? {
|
|
indexBySpine[spineIndex]
|
|
}
|
|
|
|
func pageCount(forSpineIndex spineIndex: Int) -> Int? {
|
|
entry(forSpineIndex: spineIndex)?.pageCount
|
|
}
|
|
|
|
var totalChapters: Int { entries.count }
|
|
|
|
struct Builder {
|
|
|
|
private var items: [(spineIndex: Int, href: String, title: String, pageCount: Int, fragmentOffsets: [String: Int])] = []
|
|
|
|
mutating func add(spineIndex: Int, href: String, title: String, pageCount: Int, fragmentOffsets: [String: Int]) {
|
|
items.append((spineIndex, href, title, pageCount, fragmentOffsets))
|
|
}
|
|
|
|
func build() -> RDEPUBBookPageMap {
|
|
|
|
let sorted = items.sorted { $0.spineIndex < $1.spineIndex }
|
|
var entries: [RDEPUBBookPageMapEntry] = []
|
|
var absolutePageStart = 0
|
|
for item in sorted {
|
|
entries.append(RDEPUBBookPageMapEntry(
|
|
spineIndex: item.spineIndex,
|
|
href: item.href,
|
|
title: item.title,
|
|
pageCount: item.pageCount,
|
|
absolutePageStart: absolutePageStart,
|
|
fragmentOffsets: item.fragmentOffsets
|
|
))
|
|
absolutePageStart += item.pageCount
|
|
}
|
|
return RDEPUBBookPageMap(entries: entries)
|
|
}
|
|
}
|
|
}
|