// RDEPUBWebView+FixedLayout.swift // RDEPUBWebView 的固定版式渲染扩展 // 处理固定版式(Fixed Layout)的加载流程:生成 HTML 模板、 // 计算加载签名(去重)、设置固定版式就绪回退定时器等。 import UIKit import WebKit extension RDEPUBWebView { /// 便捷方法:加载固定版式的 spread 页面 public func loadFixedSpread( parser: RDEPUBParser, spread: EPUBFixedSpread, viewportSize: CGSize, contentInset: UIEdgeInsets, backgroundColor: UIColor? ) { let request = RDEPUBRenderRequest.fixed( RDEPUBFixedRenderRequest( spread: spread, viewportSize: viewportSize, contentInset: contentInset, backgroundColorCSS: backgroundColor?.ss_hexString, fit: .page, searchPresentation: nil ) ) load(publication: parser.makePublication(), request: request) } /// 处理固定版式加载:设置状态、生成 HTML、加载到 WebView func handleFixedLayoutLoad( publication: RDEPUBPublication, request: RDEPUBFixedRenderRequest, loadSignature: String, in webView: WKWebView ) { currentPageIndex = 0 currentTotalPagesInChapter = 1 viewportSize = request.viewportSize currentPadding = request.contentInset currentFontSize = 16 currentLineHeightMultiple = 1.5 currentThemeBackgroundColor = request.backgroundColorCSS currentThemeTextColor = nil targetLocation = nil pendingHighlights = [] fixedSpread = request.spread isFixedLayout = true pendingProgressionRequest = false currentLoadSignature = loadSignature scheduleFixedLayoutReadyFallback() let html = RDEPUBFixedLayoutTemplate.html(for: request, publication: publication) RDEPUBWebViewDebug.log(debugScope, message: "load fixed spread resources=\(request.spread.resources.map(\.href).joined(separator: ",")) fit=\(request.fit.rawValue)") webView.loadHTMLString( html, baseURL: URL(string: "\(RDEPUBResourceURLSchemeHandler.scheme)://\(RDEPUBResourceURLSchemeHandler.host)/") ) } /// 生成固定版式加载签名(用于去重相同请求) func fixedLayoutLoadSignature(publicationKey: String, request: RDEPUBFixedRenderRequest) -> String { [ publicationKey, "fixed", request.spread.resources.map(\.href).joined(separator: "|"), String(format: "%.2f", request.viewportSize.width), String(format: "%.2f", request.viewportSize.height), String(format: "%.2f", request.contentInset.top), String(format: "%.2f", request.contentInset.left), String(format: "%.2f", request.contentInset.bottom), String(format: "%.2f", request.contentInset.right), request.backgroundColorCSS ?? "", request.fit.rawValue ].joined(separator: "#") } /// 调度固定版式就绪回退定时器(1 秒后如果未收到 ready 消息则手动触发渲染完成) func scheduleFixedLayoutReadyFallback() { let workItem = DispatchWorkItem { [weak self] in RDEPUBWebViewDebug.log(self?.debugScope ?? "ReaderWebView", message: "fixed ready fallback fired") self?.applySearchDecorationsIfNeeded { self?.rendered() } } fixedLayoutReadyWorkItem = workItem DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: workItem) } /// 取消固定版式就绪回退定时器 func cancelFixedLayoutReadyFallback() { fixedLayoutReadyWorkItem?.cancel() fixedLayoutReadyWorkItem = nil } }