- 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
194 lines
5.8 KiB
Swift
194 lines
5.8 KiB
Swift
import UIKit
|
|
|
|
public struct RDEPUBTextChapterPaginationDiagnostic: Equatable {
|
|
|
|
public var href: String
|
|
|
|
public var title: String
|
|
|
|
public var pageCount: Int
|
|
|
|
public var breakReasons: [RDEPUBTextPageBreakReason]
|
|
|
|
public var attachmentPageCount: Int
|
|
|
|
public var blockAdjustedPageCount: Int
|
|
|
|
public var blockKinds: [RDEPUBTextBlockKind]
|
|
|
|
public var semanticHints: [RDEPUBTextSemanticHint]
|
|
|
|
public var attachmentPlacements: [RDEPUBTextAttachmentPlacement]
|
|
|
|
public var sampleNotes: [String]
|
|
}
|
|
|
|
public struct RDEPUBTextChapterBuildResult {
|
|
|
|
public var chapter: RDEPUBTextChapter
|
|
|
|
public var resourceDiagnostics: [RDEPUBTextResourceReferenceDiagnostic]
|
|
|
|
public var paginationDiagnostic: RDEPUBTextChapterPaginationDiagnostic
|
|
|
|
public var performanceSample: RDEPUBTextPerformanceSample
|
|
|
|
public var cacheHit: Bool
|
|
}
|
|
|
|
public struct RDEPUBTextPage: Equatable {
|
|
|
|
public var absolutePageIndex: Int
|
|
|
|
public var chapterIndex: Int
|
|
|
|
public var spineIndex: Int
|
|
|
|
public var href: String
|
|
|
|
public var chapterTitle: String
|
|
|
|
public var pageIndexInChapter: Int
|
|
|
|
public var totalPagesInChapter: Int
|
|
|
|
public var chapterContent: NSAttributedString
|
|
|
|
/// Page substring derived on demand from `chapterContent` + `contentRange`.
|
|
/// Not stored: keeping a per-page substring alive roughly doubles the
|
|
/// chapter's resident text memory across the chapter cache window.
|
|
public var content: NSAttributedString {
|
|
let bounds = NSRange(location: 0, length: chapterContent.length)
|
|
let clamped = NSIntersectionRange(contentRange, bounds)
|
|
guard clamped.length > 0 else { return NSAttributedString() }
|
|
return chapterContent.attributedSubstring(from: clamped)
|
|
}
|
|
|
|
public var contentRange: NSRange
|
|
|
|
public var pageStartOffset: Int
|
|
|
|
public var pageEndOffset: Int
|
|
|
|
public var metadata: RDEPUBTextPageMetadata
|
|
}
|
|
|
|
public struct RDEPUBTextChapter: Equatable {
|
|
|
|
public var chapterIndex: Int
|
|
|
|
public var spineIndex: Int
|
|
|
|
public var href: String
|
|
|
|
public var title: String
|
|
|
|
public var attributedContent: NSAttributedString
|
|
|
|
public var fragmentOffsets: [String: Int]
|
|
|
|
public var cfiMap: RDEPUBCFIMap?
|
|
|
|
public var pageBreakReasons: [RDEPUBTextPageBreakReason]
|
|
|
|
public var pages: [RDEPUBTextPage]
|
|
}
|
|
|
|
public struct RDEPUBTextBook {
|
|
|
|
public var chapters: [RDEPUBTextChapter]
|
|
|
|
public var pages: [RDEPUBTextPage]
|
|
|
|
public let indexTable: RDEPUBTextIndexTable
|
|
|
|
public var positionConverter: RDEPUBTextPositionConverter {
|
|
RDEPUBTextPositionConverter(book: self)
|
|
}
|
|
|
|
public init(chapters: [RDEPUBTextChapter], pages: [RDEPUBTextPage]) {
|
|
self.chapters = chapters
|
|
self.pages = pages
|
|
self.indexTable = RDEPUBTextIndexTable(chapters: chapters)
|
|
}
|
|
|
|
public static func == (lhs: RDEPUBTextBook, rhs: RDEPUBTextBook) -> Bool {
|
|
lhs.chapters == rhs.chapters && lhs.pages == rhs.pages
|
|
}
|
|
|
|
public func chapterData(for href: String) -> RDEPUBChapterData? {
|
|
guard let chapter = chapters.first(where: { $0.href == href }) else { return nil }
|
|
return RDEPUBChapterData(chapter: chapter, indexTable: indexTable)
|
|
}
|
|
|
|
public func chapterData(forSpineIndex spineIndex: Int) -> RDEPUBChapterData? {
|
|
guard let chapter = chapters.first(where: { $0.spineIndex == spineIndex }) else { return nil }
|
|
return RDEPUBChapterData(chapter: chapter, indexTable: indexTable)
|
|
}
|
|
|
|
public func chapterData(atChapterIndex index: Int) -> RDEPUBChapterData? {
|
|
guard chapters.indices.contains(index) else { return nil }
|
|
return RDEPUBChapterData(chapter: chapters[index], indexTable: indexTable)
|
|
}
|
|
|
|
public func chapterData(forPageNumber pageNumber: Int) -> RDEPUBChapterData? {
|
|
guard let page = page(at: pageNumber) else { return nil }
|
|
return chapterData(forSpineIndex: page.spineIndex)
|
|
}
|
|
|
|
public func chapterData(
|
|
for location: RDEPUBLocation,
|
|
resolver: RDEPUBResourceResolver,
|
|
bookIdentifier: String?
|
|
) -> RDEPUBChapterData? {
|
|
guard let normalizedLocation = resolver.normalizedLocation(location, bookIdentifier: bookIdentifier) else {
|
|
return nil
|
|
}
|
|
return chapterData(for: normalizedLocation.href)
|
|
}
|
|
|
|
public var chapterInfos: [EPUBChapterInfo] {
|
|
chapters.map { chapter in
|
|
EPUBChapterInfo(
|
|
spineIndex: chapter.spineIndex,
|
|
title: chapter.title,
|
|
pageCount: chapter.pages.count
|
|
)
|
|
}
|
|
}
|
|
|
|
public func page(at pageNumber: Int) -> RDEPUBTextPage? {
|
|
guard pageNumber > 0, pages.indices.contains(pageNumber - 1) else {
|
|
return nil
|
|
}
|
|
return pages[pageNumber - 1]
|
|
}
|
|
|
|
public func pageNumber(for location: RDEPUBLocation, resolver: RDEPUBResourceResolver, bookIdentifier: String?) -> Int? {
|
|
guard let normalizedLocation = resolver.normalizedLocation(location, bookIdentifier: bookIdentifier),
|
|
let chapterData = chapterData(for: normalizedLocation.href) else {
|
|
return nil
|
|
}
|
|
|
|
if let anchor = normalizedLocation.rangeAnchor?.start,
|
|
let page = positionConverter.pageNumber(for: anchor) {
|
|
return page
|
|
}
|
|
|
|
if let anchor = indexTable.anchor(for: normalizedLocation),
|
|
let page = positionConverter.pageNumber(for: anchor) {
|
|
return page
|
|
}
|
|
|
|
return chapterData.pageNumber(for: normalizedLocation)
|
|
}
|
|
|
|
public func location(forPageNumber pageNumber: Int, bookIdentifier: String?) -> RDEPUBLocation? {
|
|
guard let chapterData = chapterData(forPageNumber: pageNumber),
|
|
let page = page(at: pageNumber) else {
|
|
return nil
|
|
}
|
|
return chapterData.location(forPage: page, bookIdentifier: bookIdentifier)
|
|
}
|
|
}
|