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 public var content: NSAttributedString 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) } }