Files
ReadViewSDK/Sources/RDReaderView/EPUBUI/ReaderController/ChapterRuntime/RDEPUBLocationConverter.swift
T

111 lines
4.0 KiB
Swift

import Foundation
struct RDEPUBLocationConverter {
// MARK: - 主路径:先构建目标章,拿到真实长度后再精确转换
/// 旧版 RDEPUBLocation -> 新版 RDEPUBChapterLocation
/// 主迁移路径:要求先构建目标章,用真实 chapterLength 做精确转换
/// 仅在无法获取章节长度时才降级到粗估 fallback
static func convert(
legacy location: RDEPUBLocation,
parser: RDEPUBParser,
publication: RDEPUBPublication,
chapterLengthProvider: ((Int) -> Int?)? = nil
) -> RDEPUBChapterLocation? {
// 1. 从 href 找到 spineIndex
guard let spineItem = publication.spine.first(where: {
$0.href == location.href || $0.href.contains(location.href)
}) else { return nil }
let spineIndex = publication.spine.firstIndex(of: spineItem) ?? 0
// 2. 优先用 fragmentID 定位(最精确,不受 progression 精度影响)
if let fragmentID = location.fragment {
return RDEPUBChapterLocation(
spineIndex: spineIndex,
chapterOffset: 0, // fragmentID 由 chapterOffsetMap 精确解析
fragmentID: fragmentID,
progressionInChapter: location.progression
)
}
// 3. 有 chapterLength 时做精确转换
if let provider = chapterLengthProvider,
let chapterLength = provider(spineIndex), chapterLength > 0 {
return convert(
legacy: location,
spineIndex: spineIndex,
chapterLength: chapterLength
)
}
// 4. Fallback:无法获取章节长度时的粗估(仅作临时降级)
let estimatedOffset = Int(location.progression * 10000)
return RDEPUBChapterLocation(
spineIndex: spineIndex,
chapterOffset: estimatedOffset,
fragmentID: nil,
progressionInChapter: location.progression,
schemaVersion: 1 // 标记为降级结果,后续可被精确值覆盖
)
}
/// 精确转换:已知章节实际长度
static func convert(
legacy location: RDEPUBLocation,
spineIndex: Int,
chapterLength: Int
) -> RDEPUBChapterLocation? {
let offset = Int(location.progression * Double(chapterLength))
return RDEPUBChapterLocation(
spineIndex: spineIndex,
chapterOffset: offset,
fragmentID: location.fragment,
progressionInChapter: location.progression,
schemaVersion: 2
)
}
/// 从已构建的 RDEPUBRuntimeChapter 做精确转换(推荐迁移路径)
static func convert(
legacy location: RDEPUBLocation,
chapter: RDEPUBRuntimeChapter
) -> RDEPUBChapterLocation? {
// 优先用 fragmentID
if let fragmentID = location.fragment,
let fragmentOffset = chapter.chapterOffsetMap.chapterOffset(forFragmentID: fragmentID) {
return RDEPUBChapterLocation(
spineIndex: chapter.spineIndex,
chapterOffset: fragmentOffset,
fragmentID: fragmentID,
progressionInChapter: nil,
schemaVersion: 2
)
}
// 用 progression + 真实长度
let chapterLength = chapter.typesetAttributedString.length
return convert(
legacy: location,
spineIndex: chapter.spineIndex,
chapterLength: chapterLength
)
}
/// 新版 -> 旧版(兼容外部接口)
static func toLegacy(
chapterLocation: RDEPUBChapterLocation,
href: String,
chapterLength: Int
) -> RDEPUBLocation {
let progression = chapterLength > 0
? Double(chapterLocation.chapterOffset) / Double(chapterLength)
: 0
return RDEPUBLocation(
href: href,
progression: min(max(progression, 0), 1),
fragment: chapterLocation.fragmentID
)
}
}