Files
ReadViewSDK/Sources/RDReaderView/EPUBCore/RDEPUBAssetRepository.swift
T
2026-05-26 09:19:37 +08:00

87 lines
3.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.
// RDEPUBAssetRepository.swift
// EPUBCore 静态资源仓库
// 负责从资源包中加载 EPUB 渲染所需的 JS 桥接脚本和固定版式 HTML 模板,
// 支持模板变量替换({{token}} 占位符)。
import Foundation
/// EPUB 内置资源枚举,定义桥接脚本和固定版式模板的文件名及扩展名
enum RDEPUBAsset: String {
/// Rangy core 等价桥接脚本,负责提供 Web range/selection 基础能力
case rangyCoreScript = "rangy-core"
/// Rangy serializer 等价桥接脚本,负责 DOM range 序列化/反序列化
case rangySerializerScript = "rangy-serializer"
/// WXRead 对齐的动态 CSS 注入脚本
case cssInjectorScript = "cssInjector"
/// WXRead 对齐的 WeReadApi JS-Native 桥接脚本
case weReadAPIScript = "WeReadApi"
/// JS 桥接脚本(epub-bridge.js),注入到 WebView 中实现原生-JS 通信
case bridgeScript = "epub-bridge"
/// 固定版式 HTML 模板(epub-fixed-layout.html),用于渲染 pre-paginated 类型的 EPUB
case fixedLayoutTemplate = "epub-fixed-layout"
/// WXRead 对齐的 default.css
case wxReadDefaultCSS = "wxread-default"
/// WXRead 对齐的 replace.css
case wxReadReplaceCSS = "wxread-replace"
/// WXRead 对齐的 dark.css
case wxReadDarkCSS = "wxread-dark"
/// WXRead 对齐的 replaceForLatinLanguageBook.css
case wxReadLatinReplaceCSS = "wxread-replace-latin"
/// 对应的文件扩展名
var fileExtension: String {
switch self {
case .rangyCoreScript, .rangySerializerScript, .cssInjectorScript, .weReadAPIScript, .bridgeScript:
return "js"
case .fixedLayoutTemplate:
return "html"
case .wxReadDefaultCSS, .wxReadReplaceCSS, .wxReadDarkCSS, .wxReadLatinReplaceCSS:
return "css"
}
}
}
/// 静态资源加载器,负责从资源包中读取模板文件并执行变量替换
enum RDEPUBAssetRepository {
/// 加载指定资源文件的内容,并将 {{key}} 占位符替换为对应的值
/// - Parameters:
/// - asset: 要加载的资源类型
/// - replacements: 模板变量字典,key 为占位符名称,value 为替换值
/// - Returns: 替换后的文件内容字符串
static func string(for asset: RDEPUBAsset, replacements: [String: String] = [:]) -> String {
guard let url = resourceBundle.url(forResource: asset.rawValue, withExtension: asset.fileExtension),
var content = try? String(contentsOf: url, encoding: .utf8) else {
assertionFailure("Missing EPUB asset: \(asset.rawValue).\(asset.fileExtension)")
return ""
}
for (token, value) in replacements {
content = content.replacingOccurrences(of: "{{\(token)}}", with: value)
}
return content
}
/// 获取资源所在的 Bundle(优先使用已解析的子 bundle,兜底到宿主 bundle
private static var resourceBundle: Bundle {
if let bundle = resolvedBundle {
return bundle
}
return Bundle(for: RDEPUBAssetBundleToken.self)
}
/// 延迟解析 RDReaderViewAssets.bundle 的位置,在宿主 bundle 和所有 framework 中查找
private static var resolvedBundle: Bundle? = {
let hostBundles = [Bundle(for: RDEPUBAssetBundleToken.self), Bundle.main] + Bundle.allFrameworks + Bundle.allBundles
for hostBundle in hostBundles {
if let url = hostBundle.url(forResource: "RDReaderViewAssets", withExtension: "bundle"),
let bundle = Bundle(url: url) {
return bundle
}
}
return nil
}()
}
/// 用于定位当前模块 Bundle 的标记类
private final class RDEPUBAssetBundleToken {}