Files
ReadViewSDK/Sources/RDEpubReaderView/EPUBCore/RDEPUBResourceDataProvider.swift
T
shenlei d7fcda345d refactor: rename RDReaderView -> RDEpubReaderView, update pod config and docs
- 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
2026-07-10 19:44:53 +09:00

64 lines
2.7 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 资源数据提供者。
///
/// 宿主 App 实现该协议以支持“解压目录内单文件加密”的 EPUB:
/// SDK 读取章节 HTML、图片、内联 CSS 等资源时会优先向 provider 索取解密后的数据,
/// provider 返回 nil 表示该文件无需解密,SDK 回退为直接读取磁盘明文
/// (明文书、或加密书内的明文文件均走该兜底路径,因此对明文书零影响)。
///
/// 密钥推导与解密算法完全由宿主实现,SDK 不持有任何密钥。
///
/// 注意:方法会在排版 / 渲染的后台队列被同步调用,实现必须线程安全,
/// 且除必要的文件 I/O 与解密计算外不得阻塞(例如不要在其中发起网络请求)。
public protocol RDEPUBResourceDataProvider: AnyObject {
/// 返回指定文件解密后的数据。
/// - Parameter fileURL: 解压目录内资源文件的绝对路径
/// - Returns: 解密后的数据;返回 nil 表示该文件不需要解密,由 SDK 直读磁盘
func resourceData(at fileURL: URL) -> Data?
}
/// 加密资源访问登记表。
///
/// DTCoreText 在解析 `<img>` 时由自己内部加载图片文件,拿不到 parser 实例,
/// 因此这里以弱引用登记所有启用了 provider 的 parser
/// 供 `RDEPUBDecryptingImageAttachment` 按文件路径反查所属书籍的 provider。
enum RDEPUBResourceAccessRegistry {
private struct WeakParserBox {
weak var parser: RDEPUBParser?
}
private static let lock = NSLock()
private static var boxes: [WeakParserBox] = []
/// 登记启用了 provider 的 parser(重复登记会被去重,弱引用失效项顺带清理)
static func register(_ parser: RDEPUBParser) {
lock.lock()
defer { lock.unlock() }
boxes.removeAll { $0.parser == nil || $0.parser === parser }
boxes.append(WeakParserBox(parser: parser))
#if canImport(DTCoreText)
RDEPUBDecryptingImageAttachment.registerTagClassIfNeeded()
#endif
}
/// 按文件路径反查 provider 并返回解密数据;
/// 仅当文件位于某个已登记 parser 的解压目录内才会命中
static func providedResourceData(at fileURL: URL) -> Data? {
let targetPath = fileURL.resolvingSymlinksInPath().standardizedFileURL.path
lock.lock()
let parsers = boxes.compactMap(\.parser)
lock.unlock()
for parser in parsers {
guard let rootURL = parser.extractionRootURL else { continue }
let rootPath = rootURL.resolvingSymlinksInPath().standardizedFileURL.path
guard targetPath.hasPrefix(rootPath + "/") else { continue }
return parser.providedResourceData(at: fileURL)
}
return nil
}
}