// // RDEPUBRenderRequest.swift // RDReaderView // // EPUBCore 层渲染请求模型定义文件。 // 包含渲染所需的展示样式(PresentationStyle)、可重排/固定版式渲染请求(RenderRequest)、 // 固定版式适配模式(Fit)和 Spread 模式(SpreadMode)。 // 由 RDEPUBPreferences 构建,传递给 RDEPUBWebView 执行渲染。 // import UIKit /// 固定版式页面适配模式 /// - auto: 自动选择最佳适配策略 /// - page: 适配整页(缩放至屏幕大小) /// - width: 适配宽度(按宽度等比缩放) public enum RDEPUBFixedLayoutFit: String, Codable { case auto case page case width } /// 固定版式 Spread(双页)显示模式 /// - automatic: 根据设备方向和屏幕尺寸自动判断 /// - always: 始终启用双页显示 /// - never: 始终单页显示 public enum RDEPUBFixedLayoutSpreadMode: String, Codable { case automatic case always case never } /// 渲染展示样式,WebView 和 Paginator 共用的视觉参数 /// 由 RDEPUBPreferences 根据用户设置构建 public struct RDEPUBPresentationStyle: Equatable { /// 视口尺寸(容器的可见区域大小) public var viewportSize: CGSize /// 内容内边距(上下左右的安全区域偏移) public var contentInsets: UIEdgeInsets /// 字号(pt) public var fontSize: CGFloat /// 行距倍数(1.0 为默认行距) public var lineHeightMultiple: CGFloat /// 栏数,对标 WXRead 的多栏分页配置 public var numberOfColumns: Int /// 栏间距,对标 WXRead 的 columnGap public var columnGap: CGFloat /// 主题背景色(CSS 颜色值,如 "#FFFFFF") public var themeBackgroundColor: String? /// 主题文字色(CSS 颜色值,如 "#000000") public var themeTextColor: String? public init( viewportSize: CGSize, contentInsets: UIEdgeInsets, fontSize: CGFloat, lineHeightMultiple: CGFloat, numberOfColumns: Int = 1, columnGap: CGFloat = 20, themeBackgroundColor: String? = nil, themeTextColor: String? = nil ) { self.viewportSize = viewportSize self.contentInsets = contentInsets self.fontSize = fontSize self.lineHeightMultiple = lineHeightMultiple self.numberOfColumns = max(1, numberOfColumns) self.columnGap = max(0, columnGap) self.themeBackgroundColor = themeBackgroundColor self.themeTextColor = themeTextColor } } /// 可重排内容渲染请求,包含加载和渲染一个 reflowable spine 项所需的全部参数 public struct RDEPUBReflowableRenderRequest: Equatable { /// spine 索引 public var spineIndex: Int /// 资源路径 public var href: String /// 目标页在章节中的索引 public var pageIndex: Int /// 该章节的总页数 public var totalPagesInChapter: Int /// 展示样式 public var presentation: RDEPUBPresentationStyle /// 目标跳转位置(用于恢复阅读位置或锚点跳转) public var targetLocation: RDEPUBLocation? /// 目标高亮的 DOM Range 信息,用于 Web 渲染路径下的精确跳转 public var targetHighlightRangeInfo: String? /// 高亮列表 public var highlights: [RDEPUBHighlight] /// 搜索结果展示信息 public var searchPresentation: RDEPUBSearchPresentation? public init( spineIndex: Int, href: String, pageIndex: Int, totalPagesInChapter: Int, presentation: RDEPUBPresentationStyle, targetLocation: RDEPUBLocation? = nil, targetHighlightRangeInfo: String? = nil, highlights: [RDEPUBHighlight] = [], searchPresentation: RDEPUBSearchPresentation? = nil ) { self.spineIndex = spineIndex self.href = href self.pageIndex = pageIndex self.totalPagesInChapter = totalPagesInChapter self.presentation = presentation self.targetLocation = targetLocation self.targetHighlightRangeInfo = targetHighlightRangeInfo self.highlights = highlights self.searchPresentation = searchPresentation } } /// 固定版式渲染请求,包含加载和渲染一个 fixed spread 所需的全部参数 public struct RDEPUBFixedRenderRequest: Equatable { /// 要渲染的 spread(包含 1-2 个资源) public var spread: EPUBFixedSpread /// 视口尺寸 public var viewportSize: CGSize /// 内容内边距 public var contentInset: UIEdgeInsets /// 背景色 CSS 值 public var backgroundColorCSS: String? /// 页面适配模式(auto/page/width) public var fit: RDEPUBFixedLayoutFit /// 搜索结果展示信息 public var searchPresentation: RDEPUBSearchPresentation? public init( spread: EPUBFixedSpread, viewportSize: CGSize, contentInset: UIEdgeInsets, backgroundColorCSS: String? = nil, fit: RDEPUBFixedLayoutFit = .page, searchPresentation: RDEPUBSearchPresentation? = nil ) { self.spread = spread self.viewportSize = viewportSize self.contentInset = contentInset self.backgroundColorCSS = backgroundColorCSS self.fit = fit self.searchPresentation = searchPresentation } } /// 渲染请求枚举,统一可重排和固定版式两种渲染模式 /// RDEPUBWebView 根据此请求类型选择对应的加载和渲染路径 public enum RDEPUBRenderRequest: Equatable { /// 可重排内容渲染请求 case reflowable(RDEPUBReflowableRenderRequest) /// 固定版式渲染请求 case fixed(RDEPUBFixedRenderRequest) /// 是否为固定版式渲染 public var isFixedLayout: Bool { if case .fixed = self { return true } return false } /// 主资源的 spine 索引 public var primarySpineIndex: Int { switch self { case .reflowable(let request): return request.spineIndex case .fixed(let request): return request.spread.primaryResource.spineIndex } } /// 主资源的路径 public var primaryHref: String { switch self { case .reflowable(let request): return request.href case .fixed(let request): return request.spread.primaryResource.href } } }