ReadViewSDK/Sources/RDEpubReaderView/EPUBCore/RDEPUBWebView+Reflowable.swift
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

162 lines
6.6 KiB
Swift

import UIKit
import WebKit
extension RDEPUBWebView {
public func loadPage(
parser: RDEPUBParser,
spineIndex: Int,
pageIndex: Int,
totalPagesInChapter: Int,
viewportSize: CGSize,
padding: UIEdgeInsets,
fontSize: CGFloat,
lineHeightMultiple: CGFloat,
themeBackgroundColor: String?,
themeTextColor: String?,
targetLocation: RDEPUBLocation?,
highlights: [RDEPUBHighlight]
) {
let href = parser.spine.indices.contains(spineIndex) ? parser.spine[spineIndex].href : ""
let request = RDEPUBRenderRequest.reflowable(
RDEPUBReflowableRenderRequest(
spineIndex: spineIndex,
href: href,
pageIndex: pageIndex,
totalPagesInChapter: totalPagesInChapter,
presentation: RDEPUBPresentationStyle(
viewportSize: viewportSize,
contentInsets: padding,
fontSize: fontSize,
lineHeightMultiple: lineHeightMultiple,
themeBackgroundColor: themeBackgroundColor,
themeTextColor: themeTextColor
),
targetLocation: targetLocation,
highlights: highlights,
searchPresentation: nil
)
)
load(publication: parser.makePublication(), request: request)
}
func handleReflowableLoad(
publication: RDEPUBPublication,
request: RDEPUBReflowableRenderRequest,
loadSignature: String,
in webView: WKWebView
) {
guard publication.spine.indices.contains(request.spineIndex),
let requestURL = publication.resourceResolver.resourceURL(forRelativePath: request.href) else {
return
}
currentPageIndex = request.pageIndex
currentTotalPagesInChapter = max(1, request.totalPagesInChapter)
viewportSize = request.presentation.viewportSize
currentPadding = request.presentation.contentInsets
currentFontSize = request.presentation.fontSize
currentLineHeightMultiple = request.presentation.lineHeightMultiple
currentThemeBackgroundColor = request.presentation.themeBackgroundColor
currentThemeTextColor = request.presentation.themeTextColor
targetLocation = request.targetLocation
pendingHighlights = request.highlights
fixedSpread = nil
isFixedLayout = false
pendingProgressionRequest = true
if currentLoadSignature == loadSignature {
if didRenderCurrentRequest || webView.isLoading {
let reason = didRenderCurrentRequest ? "already-rendered" : "still-loading"
RDEPUBWebViewDebug.log(debugScope, message: "skip duplicate request href=\(request.href) page=\(request.pageIndex) reason=\(reason)")
return
}
if webView.url == requestURL {
RDEPUBWebViewDebug.log(debugScope, message: "reuse existing document href=\(request.href) page=\(request.pageIndex)")
applyPresentation()
return
}
}
currentLoadSignature = loadSignature
RDEPUBWebViewDebug.logNavigationEvent(debugScope, webView: webView, event: "load-request", url: requestURL)
webView.load(URLRequest(url: requestURL))
}
func reflowableLoadSignature(publicationKey: String, request: RDEPUBReflowableRenderRequest) -> String {
let targetSignature = [
request.targetLocation?.href ?? "",
String(request.targetLocation?.progression ?? 0),
String(request.targetLocation?.lastProgression ?? 0),
request.targetLocation?.fragment ?? "",
request.targetHighlightRangeInfo ?? ""
].joined(separator: "|")
let highlightSignature = request.highlights
.map { [$0.id, $0.rangeInfo ?? "", $0.color, $0.style.rawValue].joined(separator: "|") }
.joined(separator: ",")
let searchSignature = [
request.searchPresentation?.keyword ?? "",
request.searchPresentation?.resources.map {
[
$0.href,
String($0.matchCount),
String($0.activeLocalMatchIndex ?? -1)
].joined(separator: "|")
}.joined(separator: ",") ?? ""
].joined(separator: "#")
let presentation = request.presentation
return [
publicationKey,
"reflowable",
String(request.spineIndex),
request.href,
String(request.pageIndex),
String(request.totalPagesInChapter),
String(format: "%.2f", presentation.viewportSize.width),
String(format: "%.2f", presentation.viewportSize.height),
String(format: "%.2f", presentation.contentInsets.top),
String(format: "%.2f", presentation.contentInsets.left),
String(format: "%.2f", presentation.contentInsets.bottom),
String(format: "%.2f", presentation.contentInsets.right),
String(format: "%.2f", presentation.fontSize),
String(format: "%.2f", presentation.lineHeightMultiple),
String(presentation.numberOfColumns),
String(format: "%.2f", presentation.columnGap),
presentation.themeBackgroundColor ?? "",
presentation.themeTextColor ?? "",
targetSignature,
highlightSignature,
searchSignature
].joined(separator: "#")
}
func applyPresentation() {
guard let webView, let currentRenderRequest else { return }
guard case .reflowable(let request) = currentRenderRequest else {
return
}
let script = RDEPUBJavaScriptBridge.applyPresentationScript(for: request)
RDEPUBWebViewDebug.logJavaScript(
debugScope,
webView: webView,
action: "applyPresentation",
details: "spine=\(request.spineIndex) page=\(request.pageIndex) total=\(request.totalPagesInChapter)"
)
webView.evaluateJavaScript(script) { [weak self] _, error in
guard let self else { return }
if let error {
RDEPUBWebViewDebug.logJavaScript(self.debugScope, webView: webView, action: "applyPresentationFailed", details: error.localizedDescription)
self.delegate?.epubWebView(self, didLogJavaScriptError: error.localizedDescription)
return
}
self.applySearchDecorationsIfNeeded {
self.rendered()
}
}
}
}