- 新增 RDEPUBReaderSearchCoordinator 与 RDEPUBSelectionState 管理搜索和选中状态 - 新增 BookmarkChromeStateTests、NavigationBackwardTests、SelectionAnnotateTests 等 UI 测试 - 新增多个边界测试 epub 样本(损坏结构、空归档、缺失文件、流式外链验证) - 重构阅读器 chrome 状态管理,统一 tool bar 与 search bar 交互 - 优化大书分页缓存策略(RDEPUBChapterSummaryDiskCache、RDEPUBPageCountCache) - 移除废弃的 RDEPUBLocationConverter 和 RDEPUBPageBreakPolicy - 更新 epub-bridge.js 与 JS bridge 通信协议 - 全面更新现有 UI 测试以适配新的 helper 和状态管理
115 lines
4.5 KiB
Swift
115 lines
4.5 KiB
Swift
// RDEPUBPreferences.swift
|
||
// EPUB 阅读偏好设置
|
||
// 封装字号、行高、内容内边距、主题颜色、固定版式适配模式等展示参数,
|
||
// 并提供从偏好设置生成渲染请求和展示样式的便捷方法。
|
||
|
||
import UIKit
|
||
|
||
/// EPUB 阅读偏好设置,包含所有影响渲染效果的展示参数
|
||
public struct RDEPUBPreferences: Equatable {
|
||
/// 字体大小(pt)
|
||
public var fontSize: CGFloat
|
||
/// 行高倍数(如 1.5 表示 1.5 倍行高)
|
||
public var lineHeightMultiple: CGFloat
|
||
/// 重排模式下的内容内边距
|
||
public var reflowableContentInsets: UIEdgeInsets
|
||
/// 固定版式下的内容内边距
|
||
public var fixedContentInset: UIEdgeInsets
|
||
/// 主题背景色(CSS 颜色值,如 "#FFFFFF")
|
||
public var themeBackgroundColor: String?
|
||
/// 主题文字色(CSS 颜色值)
|
||
public var themeTextColor: String?
|
||
/// 固定版式背景色
|
||
public var fixedBackgroundColor: String?
|
||
/// 固定版式适配模式(page/width/height/contain/cover)
|
||
public var fixedLayoutFit: RDEPUBFixedLayoutFit
|
||
/// 固定版式双页展开模式(never/always/automatic)
|
||
public var fixedLayoutSpreadMode: RDEPUBFixedLayoutSpreadMode
|
||
/// 多栏排版栏数
|
||
public var numberOfColumns: Int
|
||
/// 多栏排版栏间距
|
||
public var columnGap: CGFloat
|
||
|
||
public init(
|
||
fontSize: CGFloat,
|
||
lineHeightMultiple: CGFloat,
|
||
reflowableContentInsets: UIEdgeInsets,
|
||
fixedContentInset: UIEdgeInsets,
|
||
numberOfColumns: Int = 1,
|
||
columnGap: CGFloat = 20,
|
||
themeBackgroundColor: String? = nil,
|
||
themeTextColor: String? = nil,
|
||
fixedBackgroundColor: String? = nil,
|
||
fixedLayoutFit: RDEPUBFixedLayoutFit = .page,
|
||
fixedLayoutSpreadMode: RDEPUBFixedLayoutSpreadMode = .automatic
|
||
) {
|
||
self.fontSize = fontSize
|
||
self.lineHeightMultiple = lineHeightMultiple
|
||
self.reflowableContentInsets = reflowableContentInsets
|
||
self.fixedContentInset = fixedContentInset
|
||
self.numberOfColumns = max(1, numberOfColumns)
|
||
self.columnGap = max(0, columnGap)
|
||
self.themeBackgroundColor = themeBackgroundColor
|
||
self.themeTextColor = themeTextColor
|
||
self.fixedBackgroundColor = fixedBackgroundColor
|
||
self.fixedLayoutFit = fixedLayoutFit
|
||
self.fixedLayoutSpreadMode = fixedLayoutSpreadMode
|
||
}
|
||
|
||
/// 从偏好设置生成重排模式的展示样式
|
||
public func presentationStyle(viewportSize: CGSize) -> RDEPUBPresentationStyle {
|
||
RDEPUBPresentationStyle(
|
||
viewportSize: viewportSize,
|
||
contentInsets: reflowableContentInsets,
|
||
fontSize: fontSize,
|
||
lineHeightMultiple: lineHeightMultiple,
|
||
numberOfColumns: numberOfColumns,
|
||
columnGap: columnGap,
|
||
themeBackgroundColor: themeBackgroundColor,
|
||
themeTextColor: themeTextColor
|
||
)
|
||
}
|
||
|
||
/// 根据页面和偏好设置生成对应的渲染请求(自动区分固定版式和重排)
|
||
public func renderRequest(
|
||
for page: EPUBPage,
|
||
publication: RDEPUBPublication,
|
||
viewportSize: CGSize,
|
||
targetLocation: RDEPUBLocation? = nil,
|
||
targetHighlightRangeInfo: String? = nil,
|
||
highlights: [RDEPUBHighlight] = [],
|
||
searchPresentation: RDEPUBSearchPresentation? = nil
|
||
) -> RDEPUBRenderRequest? {
|
||
if publication.layout == .fixed, let fixedSpread = page.fixedSpread {
|
||
return .fixed(
|
||
RDEPUBFixedRenderRequest(
|
||
spread: fixedSpread,
|
||
viewportSize: viewportSize,
|
||
contentInset: fixedContentInset,
|
||
backgroundColorCSS: fixedBackgroundColor,
|
||
fit: fixedLayoutFit,
|
||
searchPresentation: searchPresentation
|
||
)
|
||
)
|
||
}
|
||
|
||
guard publication.spine.indices.contains(page.spineIndex) else {
|
||
return nil
|
||
}
|
||
|
||
return .reflowable(
|
||
RDEPUBReflowableRenderRequest(
|
||
spineIndex: page.spineIndex,
|
||
href: publication.spine[page.spineIndex].href,
|
||
pageIndex: page.pageIndexInChapter,
|
||
totalPagesInChapter: page.totalPagesInChapter,
|
||
presentation: presentationStyle(viewportSize: viewportSize),
|
||
targetLocation: targetLocation,
|
||
targetHighlightRangeInfo: targetHighlightRangeInfo,
|
||
highlights: highlights,
|
||
searchPresentation: searchPresentation
|
||
)
|
||
)
|
||
}
|
||
}
|