feat: EPUB阅读器搜索、注释、CFI模块及大书远距跳转优化

- 实现EPUB阅读器搜索功能及选中注释功能
- 优化CFI模块,修复代码审查发现的11个问题
- 实现大书远距目录跳转与后台补全优化方案
- 优化设置面板与章节运行时联动
- 重构及大量改进优化
This commit is contained in:
shenlei
2026-06-22 20:26:34 +08:00
parent f50495ad91
commit c65c190b71
178 changed files with 11380 additions and 6728 deletions
@@ -1,15 +1,9 @@
// RDEPUBParser+Archive.swift
// EPUB container.xml
// EPUB ZIP ~/Library/Caches/ssreaderview-epub/
// META-INF/container.xml OPF
import Foundation
import ZIPFoundation
extension RDEPUBParser {
/// container.xml OPFPackage Document
/// - Parameter containerURL: container.xml
/// - Returns: OPF "OEBPS/content.opf"
func parseContainerRootFile(at containerURL: URL) throws -> String {
guard let parser = XMLParser(contentsOf: containerURL) else {
throw RDEPUBParserError.invalidXML(containerURL)
@@ -30,10 +24,6 @@ extension RDEPUBParser {
return rootFilePath
}
/// EPUB ZIP
/// slug + +
/// - Parameter epubURL: EPUB
/// - Returns: URL
func extractArchiveIfNeeded(epubURL: URL) throws -> URL {
let fileManager = FileManager.default
let extractionURL = temporaryExtractionDirectory(for: epubURL)
@@ -66,17 +56,12 @@ extension RDEPUBParser {
return extractionURL
}
/// 穿
/// - Parameters:
/// - entryPath:
/// - extractionRoot:
/// - Returns: URL nil
private func validatedExtractionDestination(for entryPath: String, extractionRoot: URL) -> URL? {
//
if entryPath.hasPrefix("/") {
return nil
}
// ..
let components = entryPath.split(separator: "/", omittingEmptySubsequences: true)
if components.contains(where: { $0 == ".." }) {
return nil
@@ -84,15 +69,13 @@ extension RDEPUBParser {
let destinationURL = extractionRoot.appendingPathComponent(entryPath)
let standardizedDest = destinationURL.standardizedFileURL.path
let standardizedRoot = extractionRoot.standardizedFileURL.path
//
guard standardizedDest.hasPrefix(standardizedRoot) else {
return nil
}
return destinationURL
}
/// EPUB
/// ~/Library/Caches/ssreaderview-epub/{slug}-{fileSize}-{modifiedTimestamp}/
func temporaryExtractionDirectory(for epubURL: URL) -> URL {
let baseURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first?
.appendingPathComponent("ssreaderview-epub", isDirectory: true)
@@ -106,14 +89,12 @@ extension RDEPUBParser {
return baseURL.appendingPathComponent("\(slug)-\(fileSize)-\(signature)", isDirectory: true)
}
///
func reset() {
extractionRootURL = nil
opfURL = nil
resetPublicationState()
}
/// metadatamanifestspine
func resetPublicationState() {
metadata = RDEPUBMetadata()
manifest = [:]
@@ -122,9 +103,8 @@ extension RDEPUBParser {
}
}
/// container.xml SAX <rootfile> full-path
private final class ContainerXMLParserDelegate: NSObject, XMLParserDelegate {
/// OPF
private(set) var rootFilePath: String?
func parser(