Files
ReadViewSDK/Sources/RDReaderView/EPUBCore/RDEPUBTextAnchor.swift
T
shenandshen 54798ba578 refactor: 添加中文注释 + 优化模块结构
- 给全部 78 个 Swift 源文件添加详细的中文注释(文件级、类级、方法级)
- 删除 LegacyRDReaderController/ 死代码目录(16 文件 4592 行)
- 根目录翻页容器文件移入 ReaderView/ 目录
- Resources/ 移入 EPUBCore/Resources/(与使用者归属一致)
- RDEPUBTextIndexTable.swift 移入 EPUBTextRendering/(消除反向依赖)
- RDURLReaderController.swift 移入 EPUBUI/(入口控制器归入 UI 层)
- 更新 podspec 资源路径
2026-05-25 10:19:14 +08:00

87 lines
3.1 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// RDEPUBTextAnchor.swift
// EPUB 文本锚点和范围锚点
// 定义精确的文本位置锚点(RDEPUBTextAnchor,含 fileIndex/row/column/chapterOffset/fragmentID),
// 以及文本范围锚点(RDEPUBTextRangeAnchor,起止锚点对)。
// 支持 Codable 序列化(兼容 spineIndex/fileIndex 两种 key)。
import Foundation
/// 文本锚点,精确定位到 EPUB 中的某个字符位置
/// 包含文件索引、行号、列号、章节内偏移和可选的 fragment ID
public struct RDEPUBTextAnchor: Codable, Equatable {
/// 文件索引(等同于 spineIndex
public let fileIndex: Int
/// 行号
public let row: Int
/// 列号(行内偏移)
public let column: Int
/// 章节内的绝对字符偏移量
public let chapterOffset: Int
/// 最近的 fragment ID(用于 URL 锚点定位)
public let fragmentID: String?
/// spine 索引的别名(与 fileIndex 等价)
public var spineIndex: Int { fileIndex }
public init(
fileIndex: Int,
row: Int,
column: Int,
chapterOffset: Int,
fragmentID: String? = nil
) {
self.fileIndex = fileIndex
self.row = row
self.column = column
self.chapterOffset = chapterOffset
self.fragmentID = fragmentID
}
enum CodingKeys: String, CodingKey {
case fileIndex
case spineIndex
case row
case column
case chapterOffset
case fragmentID
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let decodedFileIndex = try container.decodeIfPresent(Int.self, forKey: .fileIndex)
?? container.decode(Int.self, forKey: .spineIndex)
self.fileIndex = decodedFileIndex
self.row = try container.decodeIfPresent(Int.self, forKey: .row) ?? 0
self.column = try container.decodeIfPresent(Int.self, forKey: .column) ?? 0
self.chapterOffset = try container.decode(Int.self, forKey: .chapterOffset)
self.fragmentID = try container.decodeIfPresent(String.self, forKey: .fragmentID)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(fileIndex, forKey: .fileIndex)
try container.encode(row, forKey: .row)
try container.encode(column, forKey: .column)
try container.encode(chapterOffset, forKey: .chapterOffset)
try container.encodeIfPresent(fragmentID, forKey: .fragmentID)
}
}
/// 文本范围锚点,由起止锚点组成的区间
public struct RDEPUBTextRangeAnchor: Codable, Equatable {
/// 起始锚点
public let start: RDEPUBTextAnchor
/// 结束锚点
public let end: RDEPUBTextAnchor
public init(start: RDEPUBTextAnchor, end: RDEPUBTextAnchor) {
self.start = start
self.end = end
}
/// 转换为 NSRange(基于 chapterOffset
public var nsRange: NSRange {
NSRange(location: start.chapterOffset, length: max(end.chapterOffset - start.chapterOffset, 0))
}
}