Files
ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextSearchEngine.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

85 lines
3.6 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.
import Foundation
/// EPUB 文本搜索引擎:在分页书籍中执行全文搜索。
///
/// 实现 `RDEPUBSearchEngine` 协议,遍历所有章节的富文本内容,
/// 使用大小写不敏感匹配查找关键词,返回搜索结果列表。
/// 每个搜索结果包含进度位置、预览文本和语义锚点。
final class RDEPUBTextSearchEngine: RDEPUBSearchEngine {
private let textBook: RDEPUBTextBook
private let publication: RDEPUBPublication
init(textBook: RDEPUBTextBook, publication: RDEPUBPublication) {
self.textBook = textBook
self.publication = publication
}
/// 在全书中搜索关键词,返回所有匹配结果。
///
/// 搜索流程:
/// 1. 遍历所有章节
/// 2. 在每章的富文本字符串中执行大小写不敏感搜索
/// 3. 计算匹配位置的阅读进度(progression
/// 4. 生成预览文本(匹配位置前后各 12 个字符)
/// 5. 生成语义锚点(用于跨设备/跨字体精确定位)
func search(keyword: String) -> [RDEPUBSearchMatch] {
let normalizedKeyword = keyword.trimmingCharacters(in: .whitespacesAndNewlines)
guard !normalizedKeyword.isEmpty else {
return []
}
var matches: [RDEPUBSearchMatch] = []
for chapter in textBook.chapters {
guard let chapterData = textBook.chapterData(for: chapter.href) else { continue }
let source = chapter.attributedContent.string as NSString
let fullLength = source.length
guard fullLength > 0 else {
continue
}
let normalizedHref = publication.resourceResolver.normalizedHref(chapter.href) ?? chapter.href
var localMatchIndex = 0
var searchRange = NSRange(location: 0, length: fullLength)
while searchRange.length > 0 {
let foundRange = source.range(of: normalizedKeyword, options: [.caseInsensitive], range: searchRange)
guard foundRange.location != NSNotFound else {
break
}
let progressionDenominator = max(fullLength - 1, 1)
let progression = Double(foundRange.location) / Double(progressionDenominator)
matches.append(
RDEPUBSearchMatch(
href: normalizedHref,
progression: progression,
previewText: previewText(in: source, matchRange: foundRange),
localMatchIndex: localMatchIndex,
rangeLocation: foundRange.location,
rangeLength: foundRange.length,
rangeAnchor: chapterData.rangeAnchor(for: foundRange)
)
)
localMatchIndex += 1
let nextLocation = foundRange.location + max(foundRange.length, 1)
if nextLocation >= fullLength {
break
}
searchRange = NSRange(location: nextLocation, length: fullLength - nextLocation)
}
}
return matches
}
/// 生成搜索结果的预览文本:匹配位置前后各取 12 个字符
private func previewText(in text: NSString, matchRange: NSRange) -> String {
let previewRadius = 12
let start = max(matchRange.location - previewRadius, 0)
let end = min(matchRange.location + matchRange.length + previewRadius, text.length)
let range = NSRange(location: start, length: max(end - start, 0))
return text.substring(with: range).trimmingCharacters(in: .whitespacesAndNewlines)
}
}