feat(epub): complete wxread parity scope

This commit is contained in:
shen
2026-05-26 09:19:37 +08:00
parent 23182e8b5a
commit 0e7d952fe5
31 changed files with 2619 additions and 1533 deletions
@@ -106,6 +106,12 @@ public final class RDEPUBReaderController: UIViewController {
activeBookmarks
}
/// WXRead WRBookmark
public var annotations: [RDEPUBAnnotation] {
let merged = activeHighlights.map(\.annotation) + activeBookmarks.map(\.annotation)
return merged.sorted { $0.createdAt < $1.createdAt }
}
///
public var tableOfContents: [EPUBTableOfContentsItem] {
publication?.tableOfContents ?? []
@@ -164,6 +170,7 @@ public final class RDEPUBReaderController: UIViewController {
private var searchState: RDEPUBSearchState?
private var lastAppliedViewportSignature: RDEPUBViewportSignature?
private var pendingViewportChangeReason: RDEPUBViewportChangeReason?
private var pendingPresentationRestoreLocation: RDEPUBLocation?
private var isWaitingForViewportTransitionCompletion = false
/// TextBook EPUB
private var isExternalTextBook = false
@@ -289,6 +296,7 @@ public final class RDEPUBReaderController: UIViewController {
super.viewWillTransition(to: size, with: coordinator)
guard didStartInitialLoad else { return }
pendingPresentationRestoreLocation = currentVisibleLocation() ?? persistenceLocation()
isWaitingForViewportTransitionCompletion = true
coordinator.animate(alongsideTransition: nil) { [weak self] _ in
@@ -315,6 +323,7 @@ public final class RDEPUBReaderController: UIViewController {
searchState = nil
lastAppliedViewportSignature = currentViewportSignature()
pendingViewportChangeReason = nil
pendingPresentationRestoreLocation = nil
isWaitingForViewportTransitionCompletion = false
updateCurrentSelection(nil)
readerView.reloadData()
@@ -587,17 +596,23 @@ public final class RDEPUBReaderController: UIViewController {
}
private func applyReaderViewConfiguration() {
let displayTypeDidChange = readerView.currentDisplayType != configuration.displayType
let preservedLocation = displayTypeDidChange ? currentVisibleLocation() : nil
let resolvedDirection = resolvedPageDirection()
let presentationDidChange = readerView.currentDisplayType != configuration.displayType
|| readerView.landscapeDualPageEnabled != configuration.landscapeDualPageEnabled
|| readerView.pageDirection != resolvedDirection
let preservedLocation = presentationDidChange
? (pendingPresentationRestoreLocation ?? currentVisibleLocation() ?? persistenceLocation())
: nil
view.backgroundColor = configuration.theme.contentBackgroundColor
readerView.landscapeDualPageEnabled = configuration.landscapeDualPageEnabled
readerView.pageDirection = resolvedPageDirection()
readerView.pageDirection = resolvedDirection
updateReaderChrome()
if displayTypeDidChange {
if presentationDidChange {
readerView.switchReaderDisplayType(configuration.displayType)
if let preservedLocation {
_ = restoreReadingLocation(preservedLocation)
}
pendingPresentationRestoreLocation = nil
}
}
@@ -789,7 +804,8 @@ public final class RDEPUBReaderController: UIViewController {
private func repaginatePreservingCurrentLocation() {
guard publication != nil else { return }
let restoreLocation = currentVisibleLocation() ?? persistenceLocation()
let restoreLocation = pendingPresentationRestoreLocation ?? currentVisibleLocation() ?? persistenceLocation()
pendingPresentationRestoreLocation = nil
paginatePublication(restoreLocation: restoreLocation)
}
@@ -1042,6 +1058,9 @@ public final class RDEPUBReaderController: UIViewController {
settingsController.onLineHeightChange = { [weak self] lineHeightMultiple in
self?.updateConfiguration { $0.lineHeightMultiple = lineHeightMultiple }
}
settingsController.onColumnCountChange = { [weak self] numberOfColumns in
self?.updateConfiguration { $0.numberOfColumns = numberOfColumns }
}
settingsController.onDisplayTypeChange = { [weak self] displayType in
self?.updateConfiguration { $0.displayType = displayType }
}
@@ -1097,6 +1116,8 @@ public final class RDEPUBReaderController: UIViewController {
) -> Bool {
oldConfiguration.fontSize != newConfiguration.fontSize ||
oldConfiguration.lineHeightMultiple != newConfiguration.lineHeightMultiple ||
oldConfiguration.numberOfColumns != newConfiguration.numberOfColumns ||
oldConfiguration.columnGap != newConfiguration.columnGap ||
oldConfiguration.reflowableContentInsets != newConfiguration.reflowableContentInsets ||
oldConfiguration.fixedContentInset != newConfiguration.fixedContentInset ||
oldConfiguration.fixedLayoutFit != newConfiguration.fixedLayoutFit ||
@@ -1160,6 +1181,24 @@ public final class RDEPUBReaderController: UIViewController {
return nil
}
if let textBook,
let chapterData = textBook.chapterData(
for: currentLocation,
resolver: publication.resourceResolver,
bookIdentifier: currentBookIdentifier
),
let tocItem = chapterData.primaryTableOfContentsItem(
from: publication.tableOfContents,
normalizer: { publication.resourceResolver.normalizedHref($0) }
) {
return items.last { $0.href == tocItem.href } ?? items.last {
guard let normalizedItemHref = publication.resourceResolver.normalizedHref($0.href.components(separatedBy: "#").first ?? $0.href) else {
return false
}
return normalizedItemHref == normalizedCurrentHref
}
}
return items.last { item in
guard let normalizedItemHref = publication.resourceResolver.normalizedHref(item.href.components(separatedBy: "#").first ?? item.href) else {
return false
@@ -1175,12 +1214,18 @@ public final class RDEPUBReaderController: UIViewController {
items.flatMap { item in
let location = RDEPUBLocation(bookIdentifier: currentBookIdentifier, href: item.href, progression: 0)
let pageNumber: Int?
if let textBook, let publication {
pageNumber = textBook.pageNumber(
for: location,
resolver: publication.resourceResolver,
if let textBook, let publication,
let chapterData = textBook.chapterData(for: location, resolver: publication.resourceResolver, bookIdentifier: currentBookIdentifier) {
let normalizedLocation = publication.resourceResolver.normalizedLocation(
location,
bookIdentifier: currentBookIdentifier
)
) ?? location
pageNumber = chapterData.pageNumber(for: normalizedLocation)
?? textBook.pageNumber(
for: location,
resolver: publication.resourceResolver,
bookIdentifier: currentBookIdentifier
)
} else if let readingSession {
pageNumber = readingSession.pageIndex(for: location, bookIdentifier: currentBookIdentifier).map { $0 + 1 }
} else {
@@ -1242,8 +1287,8 @@ public final class RDEPUBReaderController: UIViewController {
frameWidth: derivedFrameSize.width,
frameHeight: derivedFrameSize.height,
edgeInsets: .zero,
numberOfColumns: 1,
columnGap: 20,
numberOfColumns: configuration.numberOfColumns,
columnGap: configuration.columnGap,
avoidOrphans: true,
avoidWidows: true,
avoidPageBreakInsideEnabled: true,
@@ -1646,22 +1691,12 @@ extension RDEPUBReaderController {
private func pageNumber(for searchMatch: RDEPUBSearchMatch) -> Int? {
if let chapterData = textChapterData(forNormalizedHref: searchMatch.href) {
let location = RDEPUBLocation(
bookIdentifier: currentBookIdentifier,
href: chapterData.href,
progression: searchMatch.progression,
lastProgression: searchMatch.progression,
fragment: nil,
rangeAnchor: searchMatch.rangeAnchor
)
if let pageNumber = chapterData.pageNumber(for: location) {
if let pageNumber = chapterData.pageNumber(for: searchMatch) {
return pageNumber
}
if let rangeLocation = searchMatch.rangeLocation,
let page = chapterData.pages.first(where: {
rangeLocation >= $0.pageStartOffset && rangeLocation <= $0.pageEndOffset
}) {
let page = chapterData.page(containing: rangeLocation) {
return page.absolutePageIndex + 1
}
}
@@ -1822,6 +1857,7 @@ extension RDEPUBReaderController: RDReaderDataSource, RDReaderDelegate {
public func readerViewOrientationWillChange(readerView: RDReaderView, isLandscape: Bool) {
_ = isLandscape
pendingPresentationRestoreLocation = currentVisibleLocation() ?? persistenceLocation()
}
}
@@ -1958,7 +1994,7 @@ extension RDEPUBReaderController: RDEPUBTextContentViewDelegate {
return nil
}
let location = textBook.chapterData(for: page.href)?.location(forPage: page, bookIdentifier: currentBookIdentifier)
let location = textBook.chapterData(forPageNumber: pageNumber)?.location(forPage: page, bookIdentifier: currentBookIdentifier)
?? textBook.location(forPageNumber: pageNumber, bookIdentifier: currentBookIdentifier)
guard let location else { return nil }
@@ -1986,13 +2022,7 @@ extension RDEPUBReaderController: RDEPUBTextContentViewDelegate {
}
private func nativeTextSnapshot(from textBook: RDEPUBTextBook) -> NativeTextSnapshot {
let chapters = textBook.chapters.map {
EPUBChapterInfo(
spineIndex: $0.spineIndex,
title: $0.title,
pageCount: $0.pages.count
)
}
let chapters = textBook.chapterInfos
let pages = textBook.pages.map {
EPUBPage(
spineIndex: $0.spineIndex,