157 lines
6.3 KiB
Swift
157 lines
6.3 KiB
Swift
import Foundation
|
|
|
|
final class RDEPUBReaderPaginationCoordinator {
|
|
private unowned let context: RDEPUBReaderContext
|
|
|
|
init(context: RDEPUBReaderContext) {
|
|
self.context = context
|
|
}
|
|
|
|
func paginatePublication(restoreLocation: RDEPUBLocation?) {
|
|
guard let controller = context.controller,
|
|
let parser = context.parser,
|
|
let publication = context.publication,
|
|
let readingSession = context.readingSession else {
|
|
return
|
|
}
|
|
|
|
controller.isRepaginating = true
|
|
controller.errorLabel.isHidden = true
|
|
controller.showLoading()
|
|
let token = UUID()
|
|
context.paginationToken = token
|
|
|
|
if publication.readingProfile == .textReflowable {
|
|
let pageSize = controller.currentTextPageSize()
|
|
context.lastTextPaginationPageSize = pageSize
|
|
let layoutConfig = controller.currentTextLayoutConfig(pageSize: pageSize)
|
|
let builder = context.makeTextBookBuilder(layoutConfig: layoutConfig)
|
|
let renderStyle = controller.currentTextRenderStyle()
|
|
|
|
DispatchQueue.global(qos: .userInitiated).async { [weak controller] in
|
|
guard let controller else { return }
|
|
do {
|
|
let textBook = try builder.build(
|
|
parser: parser,
|
|
publication: publication,
|
|
pageSize: pageSize,
|
|
style: renderStyle
|
|
)
|
|
DispatchQueue.main.async {
|
|
guard self.context.paginationToken == token else { return }
|
|
self.context.runtime?.applyTextBook(textBook, restoreLocation: restoreLocation)
|
|
}
|
|
} catch {
|
|
DispatchQueue.main.async {
|
|
guard self.context.paginationToken == token else { return }
|
|
self.context.handle(error: error)
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
if publication.layout == .fixed {
|
|
let snapshot = readingSession.makePaginationSnapshot(
|
|
pageCounts: Array(repeating: 1, count: publication.spine.count),
|
|
preferences: controller.currentPreferences(),
|
|
layoutContext: controller.currentLayoutContext()
|
|
)
|
|
context.runtime?.applyPaginationSnapshot(snapshot, restoreLocation: restoreLocation)
|
|
return
|
|
}
|
|
|
|
let paginator = context.makePaginator()
|
|
context.paginator = paginator
|
|
paginator.calculate(
|
|
parser: parser,
|
|
hostingView: controller.ensurePaginationHostView(),
|
|
presentation: controller.currentPreferences().presentationStyle(viewportSize: controller.currentLayoutContext().viewportSize)
|
|
) { [weak controller] pageCounts in
|
|
guard let controller, self.context.paginationToken == token else { return }
|
|
let snapshot = readingSession.makePaginationSnapshot(
|
|
pageCounts: pageCounts,
|
|
preferences: controller.currentPreferences(),
|
|
layoutContext: controller.currentLayoutContext()
|
|
)
|
|
self.context.paginator = nil
|
|
self.context.runtime?.applyPaginationSnapshot(snapshot, restoreLocation: restoreLocation)
|
|
}
|
|
}
|
|
|
|
func applyTextBook(_ textBook: RDEPUBTextBook, restoreLocation: RDEPUBLocation?) {
|
|
guard let controller = context.controller else { return }
|
|
context.textBook = textBook
|
|
let snapshot = controller.nativeTextSnapshot(from: textBook)
|
|
context.replaceActiveSnapshot(snapshot)
|
|
|
|
guard !textBook.pages.isEmpty else {
|
|
context.handle(error: RDEPUBParserError.emptySpine)
|
|
return
|
|
}
|
|
|
|
finishPagination(restoreLocation: restoreLocation)
|
|
}
|
|
|
|
func applyPaginationSnapshot(
|
|
_ snapshot: (pages: [EPUBPage], chapters: [EPUBChapterInfo]),
|
|
restoreLocation: RDEPUBLocation?
|
|
) {
|
|
guard let controller = context.controller else { return }
|
|
context.textBook = nil
|
|
context.replaceActiveSnapshot(snapshot)
|
|
|
|
guard !snapshot.pages.isEmpty else {
|
|
context.handle(error: RDEPUBParserError.emptySpine)
|
|
return
|
|
}
|
|
|
|
finishPagination(restoreLocation: restoreLocation)
|
|
}
|
|
|
|
func finishPagination(restoreLocation: RDEPUBLocation?) {
|
|
guard let controller = context.controller,
|
|
let readerView = context.readerView else { return }
|
|
controller.isRepaginating = false
|
|
controller.hideLoading()
|
|
readerView.reloadData()
|
|
if let targetLocation = restoreLocation {
|
|
controller.restoreReadingLocation(targetLocation)
|
|
} else {
|
|
readerView.transitionToPage(pageNum: 0)
|
|
context.readingSession?.transition(to: .idle)
|
|
}
|
|
|
|
context.runtime?.viewportMonitor.processPendingChangeAfterPagination()
|
|
}
|
|
|
|
func repaginatePreservingCurrentLocation() {
|
|
guard context.publication != nil else { return }
|
|
let restoreLocation = context.runtime?.viewportMonitor.consumePendingPresentationRestoreLocation()
|
|
?? context.currentVisibleLocation()
|
|
?? context.persistenceLocation()
|
|
paginatePublication(restoreLocation: restoreLocation)
|
|
}
|
|
|
|
func refreshVisibleContentPreservingLocation() {
|
|
guard let readerView = context.readerView else { return }
|
|
let restoreLocation = context.currentVisibleLocation() ?? context.persistenceLocation()
|
|
readerView.reloadData()
|
|
if let restoreLocation {
|
|
_ = context.restoreReadingLocation(restoreLocation)
|
|
}
|
|
}
|
|
|
|
func rebuildExternalTextBook() {
|
|
guard let controller = context.controller,
|
|
let textFileURL = controller.textFileURL else { return }
|
|
let restoreLocation = context.currentVisibleLocation() ?? context.persistenceLocation()
|
|
let pageSize = controller.currentTextPageSize()
|
|
let style = controller.currentTextRenderStyle()
|
|
let builder = context.makePlainTextBookBuilder(layoutConfig: controller.currentTextLayoutConfig(pageSize: pageSize))
|
|
if let newBook = try? builder.build(textFileURL: textFileURL, pageSize: pageSize, style: style) {
|
|
context.runtime?.applyTextBook(newBook, restoreLocation: restoreLocation)
|
|
}
|
|
}
|
|
}
|