- Rename source module from RDReaderView to RDEpubReaderView - Move all source files from Sources/RDReaderView/ to Sources/RDEpubReaderView/ - Update podspec: RDReaderView.podspec -> RDEpubReaderView.podspec - Update Podfile, demo project, and CocoaPods config for new pod name - Delete old RDReaderView pod support files from ReadViewDemo/Pods - Add new RDEpubReaderView pod support files - Update documentation (API ref, architecture, UML, conventions, etc.) - Add FixedLayoutRotationTests - Update .gitignore: exclude .DS_Store, manual unpack backups, _ssoft-output
183 lines
6.3 KiB
Swift
183 lines
6.3 KiB
Swift
import Foundation
|
|
|
|
public struct RDEPUBCFIMap: Codable, Equatable {
|
|
public var href: String
|
|
public var renderVersion: Int
|
|
public var domVersion: Int
|
|
public var markers: [RDEPUBCFIMarker]
|
|
public var textAssertions: [String: RDEPUBCFITextAssertion]
|
|
public var pathRanges: [RDEPUBCFIPathRange]
|
|
public var recoveryMetadata: RDEPUBCFIRecoveryMetadata
|
|
|
|
/// Lazy O(1) lookup index for markers by cfiPath
|
|
private var markerByPath: [RDEPUBCFIPath: Int]?
|
|
|
|
public init(
|
|
href: String,
|
|
renderVersion: Int = 1,
|
|
domVersion: Int = 1,
|
|
markers: [RDEPUBCFIMarker] = [],
|
|
textAssertions: [String: RDEPUBCFITextAssertion] = [:],
|
|
pathRanges: [RDEPUBCFIPathRange] = [],
|
|
recoveryMetadata: RDEPUBCFIRecoveryMetadata = .empty
|
|
) {
|
|
self.href = href
|
|
self.renderVersion = renderVersion
|
|
self.domVersion = domVersion
|
|
self.markers = markers
|
|
self.textAssertions = textAssertions
|
|
self.pathRanges = pathRanges
|
|
self.recoveryMetadata = recoveryMetadata
|
|
}
|
|
|
|
public func marker(matching path: RDEPUBCFIPath) -> RDEPUBCFIMarker? {
|
|
if let index = markerByPath?[path] {
|
|
return markers[index]
|
|
}
|
|
return markers.first { $0.cfiPath == path }
|
|
}
|
|
|
|
/// Build or rebuild the marker lookup index. Call after mutating `markers`.
|
|
public mutating func rebuildMarkerIndex() {
|
|
markerByPath = Dictionary(
|
|
markers.enumerated().map { ($0.element.cfiPath, $0.offset) },
|
|
uniquingKeysWith: { _, last in last }
|
|
)
|
|
}
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case href
|
|
case renderVersion
|
|
case domVersion
|
|
case markers
|
|
case textAssertions
|
|
case pathRanges
|
|
case recoveryMetadata
|
|
}
|
|
|
|
public init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
href = try container.decode(String.self, forKey: .href)
|
|
renderVersion = try container.decodeIfPresent(Int.self, forKey: .renderVersion) ?? 1
|
|
domVersion = try container.decodeIfPresent(Int.self, forKey: .domVersion) ?? 1
|
|
markers = try container.decodeIfPresent([RDEPUBCFIMarker].self, forKey: .markers) ?? []
|
|
textAssertions = try container.decodeIfPresent([String: RDEPUBCFITextAssertion].self, forKey: .textAssertions) ?? [:]
|
|
pathRanges = try container.decodeIfPresent([RDEPUBCFIPathRange].self, forKey: .pathRanges) ?? []
|
|
recoveryMetadata = try container.decodeIfPresent(RDEPUBCFIRecoveryMetadata.self, forKey: .recoveryMetadata) ?? .empty
|
|
markerByPath = nil
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
try container.encode(href, forKey: .href)
|
|
try container.encode(renderVersion, forKey: .renderVersion)
|
|
try container.encode(domVersion, forKey: .domVersion)
|
|
try container.encode(markers, forKey: .markers)
|
|
try container.encode(textAssertions, forKey: .textAssertions)
|
|
try container.encode(pathRanges, forKey: .pathRanges)
|
|
try container.encode(recoveryMetadata, forKey: .recoveryMetadata)
|
|
}
|
|
|
|
public static func == (lhs: RDEPUBCFIMap, rhs: RDEPUBCFIMap) -> Bool {
|
|
lhs.href == rhs.href
|
|
&& lhs.renderVersion == rhs.renderVersion
|
|
&& lhs.domVersion == rhs.domVersion
|
|
&& lhs.markers == rhs.markers
|
|
&& lhs.textAssertions == rhs.textAssertions
|
|
&& lhs.pathRanges == rhs.pathRanges
|
|
&& lhs.recoveryMetadata == rhs.recoveryMetadata
|
|
}
|
|
}
|
|
|
|
public struct RDEPUBCFIMarker: Codable, Equatable {
|
|
public var cfiPath: RDEPUBCFIPath
|
|
public var chapterOffset: Int?
|
|
public var fragmentID: String?
|
|
public var textNodeLength: Int?
|
|
public var textNodeChecksum: UInt64?
|
|
public var normalizedTextPreview: String?
|
|
public var domSiblingSignature: String?
|
|
|
|
public init(
|
|
cfiPath: RDEPUBCFIPath,
|
|
chapterOffset: Int? = nil,
|
|
fragmentID: String? = nil,
|
|
textNodeLength: Int? = nil,
|
|
textNodeChecksum: UInt64? = nil,
|
|
normalizedTextPreview: String? = nil,
|
|
domSiblingSignature: String? = nil
|
|
) {
|
|
self.cfiPath = cfiPath
|
|
self.chapterOffset = chapterOffset
|
|
self.fragmentID = fragmentID
|
|
self.textNodeLength = textNodeLength
|
|
self.textNodeChecksum = textNodeChecksum
|
|
self.normalizedTextPreview = normalizedTextPreview
|
|
self.domSiblingSignature = domSiblingSignature
|
|
}
|
|
}
|
|
|
|
public struct RDEPUBCFIPathRange: Codable, Equatable {
|
|
public var cfiPath: RDEPUBCFIPath
|
|
public var startOffset: Int
|
|
public var endOffset: Int
|
|
public var textNodeLength: Int
|
|
|
|
public init(
|
|
cfiPath: RDEPUBCFIPath,
|
|
startOffset: Int,
|
|
endOffset: Int,
|
|
textNodeLength: Int
|
|
) {
|
|
self.cfiPath = cfiPath
|
|
self.startOffset = startOffset
|
|
self.endOffset = endOffset
|
|
self.textNodeLength = textNodeLength
|
|
}
|
|
}
|
|
|
|
public struct RDEPUBCFIRecoveryMetadata: Codable, Equatable {
|
|
public var domFingerprint: String
|
|
public var normalizedTextChecksum: String
|
|
public var tokenIndex: [RDEPUBCFITokenAnchor]
|
|
public var fragmentPathMap: [String: RDEPUBCFIPath]
|
|
|
|
public init(
|
|
domFingerprint: String,
|
|
normalizedTextChecksum: String,
|
|
tokenIndex: [RDEPUBCFITokenAnchor] = [],
|
|
fragmentPathMap: [String: RDEPUBCFIPath] = [:]
|
|
) {
|
|
self.domFingerprint = domFingerprint
|
|
self.normalizedTextChecksum = normalizedTextChecksum
|
|
self.tokenIndex = tokenIndex
|
|
self.fragmentPathMap = fragmentPathMap
|
|
}
|
|
|
|
public static let empty = RDEPUBCFIRecoveryMetadata(
|
|
domFingerprint: "",
|
|
normalizedTextChecksum: "",
|
|
tokenIndex: [],
|
|
fragmentPathMap: [:]
|
|
)
|
|
}
|
|
|
|
public struct RDEPUBCFITokenAnchor: Codable, Equatable {
|
|
public var token: String
|
|
public var occurrence: Int
|
|
public var chapterOffset: Int
|
|
public var cfiPath: RDEPUBCFIPath
|
|
|
|
public init(
|
|
token: String,
|
|
occurrence: Int,
|
|
chapterOffset: Int,
|
|
cfiPath: RDEPUBCFIPath
|
|
) {
|
|
self.token = token
|
|
self.occurrence = occurrence
|
|
self.chapterOffset = chapterOffset
|
|
self.cfiPath = cfiPath
|
|
}
|
|
}
|