import Foundation public struct RDEPUBLocation: Codable, Equatable { public var bookIdentifier: String? public var href: String public var progression: Double public var lastProgression: Double? public var fragment: String? public var rangeAnchor: RDEPUBTextRangeAnchor? public var cfi: String? public var lastCFI: String? public var rangeCFI: String? public init( bookIdentifier: String? = nil, href: String, progression: Double, lastProgression: Double? = nil, fragment: String? = nil, rangeAnchor: RDEPUBTextRangeAnchor? = nil, cfi: String? = nil, lastCFI: String? = nil, rangeCFI: String? = nil ) { self.bookIdentifier = bookIdentifier self.href = href self.progression = Self.clamp(progression) self.lastProgression = lastProgression.map(Self.clamp) self.fragment = fragment?.nilIfEmpty self.rangeAnchor = rangeAnchor self.cfi = cfi?.nilIfEmpty self.lastCFI = lastCFI?.nilIfEmpty self.rangeCFI = rangeCFI?.nilIfEmpty } public var navigationProgression: Double { let end = lastProgression ?? progression if end.isNaN || end.isInfinite { return progression } return Self.clamp((progression + end) / 2.0) } private static func clamp(_ value: Double) -> Double { guard value.isFinite else { return 0 } return min(1, max(0, value)) } } public struct RDEPUBViewportResource: Codable, Equatable { public var href: String public var spineIndex: Int public var progression: Double public var lastProgression: Double? public var fragment: String? public init( href: String, spineIndex: Int, progression: Double, lastProgression: Double? = nil, fragment: String? = nil ) { self.href = href self.spineIndex = spineIndex self.progression = progression self.lastProgression = lastProgression self.fragment = fragment } } public struct RDEPUBViewport: Codable, Equatable { public var resources: [RDEPUBViewportResource] public var visiblePageNumber: Int public var chapterIndex: Int? public var isFixedLayout: Bool public init( resources: [RDEPUBViewportResource], visiblePageNumber: Int, chapterIndex: Int? = nil, isFixedLayout: Bool ) { self.resources = resources self.visiblePageNumber = visiblePageNumber self.chapterIndex = chapterIndex self.isFixedLayout = isFixedLayout } } public struct RDEPUBReadingContext: Codable, Equatable { public var location: RDEPUBLocation public var viewport: RDEPUBViewport public var pageNumber: Int public var chapterIndex: Int? public init( location: RDEPUBLocation, viewport: RDEPUBViewport, pageNumber: Int, chapterIndex: Int? = nil ) { self.location = location self.viewport = viewport self.pageNumber = pageNumber self.chapterIndex = chapterIndex } }