import UIKit import WebKit extension RDEPUBWebView { 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?.rd_hexString, fit: .page, searchPresentation: nil ) ) load(publication: parser.makePublication(), request: request) } 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 { let searchSignature = [ request.searchPresentation?.keyword ?? "", request.searchPresentation?.resources.map { [ $0.href, String($0.matchCount), String($0.activeLocalMatchIndex ?? -1) ].joined(separator: "|") }.joined(separator: ",") ?? "" ].joined(separator: "#") return [ 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, searchSignature ].joined(separator: "#") } func scheduleFixedLayoutReadyFallback() { let workItem = DispatchWorkItem { [weak self] in RDEPUBWebViewDebug.log(self?.debugScope ?? "ReaderWebView", message: "fixed ready fallback fired") self?.applyFixedPresentationIfNeeded { self?.refreshNativeDecorationsIfNeeded { self?.rendered() } } } fixedLayoutReadyWorkItem = workItem DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: workItem) } func cancelFixedLayoutReadyFallback() { fixedLayoutReadyWorkItem?.cancel() fixedLayoutReadyWorkItem = nil } func applyFixedPresentationIfNeeded(completion: (() -> Void)? = nil) { guard let webView, let currentRenderRequest, case .fixed(let request) = currentRenderRequest else { completion?() return } let script = RDEPUBJavaScriptBridge.applyFixedPresentationScript(for: request) webView.evaluateJavaScript(script) { [weak self] _, error in guard let self else { completion?() return } if let error { self.delegate?.epubWebView(self, didLogJavaScriptError: error.localizedDescription) } self.applySearchDecorationsIfNeeded(completion: completion) } } }