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
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user