import UIKit public enum RDEPUBTextRenderingEngine: Equatable { case dtCoreText } public enum RDEPUBReaderFontChoice: String, Codable, CaseIterable, Equatable { case system case serif case rounded case monospaced public var displayName: String { switch self { case .system: return "系统" case .serif: return "宋体" case .rounded: return "圆体" case .monospaced: return "等宽" } } public func font(ofSize size: CGFloat) -> UIFont { switch self { case .system: return UIFont.systemFont(ofSize: size) case .serif: let descriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body) .withDesign(.serif) ?? UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body) return UIFont(descriptor: descriptor, size: size) case .rounded: let descriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body) .withDesign(.rounded) ?? UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body) return UIFont(descriptor: descriptor, size: size) case .monospaced: return UIFont.monospacedSystemFont(ofSize: size, weight: .regular) } } } public struct RDEPUBReaderConfiguration: Equatable { public var fontSize: CGFloat public var lineHeightMultiple: CGFloat public var fontChoice: RDEPUBReaderFontChoice public var numberOfColumns: Int public var columnGap: CGFloat public var displayType: RDReaderView.DisplayType public var landscapeDualPageEnabled: Bool public var showsTableOfContents: Bool public var allowsHighlights: Bool public var showsSettingsPanel: Bool public var reflowableContentInsets: UIEdgeInsets public var fixedContentInset: UIEdgeInsets public var theme: RDEPUBReaderTheme public var darkImageAdjustmentEnabled: Bool public var darkImageBlendRatio: CGFloat public var fixedLayoutFit: RDEPUBFixedLayoutFit public var fixedLayoutSpreadMode: RDEPUBFixedLayoutSpreadMode public var textRenderingEngine: RDEPUBTextRenderingEngine public var onDemandChapterWindowSize: Int public var metadataParsingConcurrency: Int public var jumpSessionPolicy: RDEPUBJumpSessionPolicy public var allowedExternalURLSchemes: Set public var requiresExternalLinkConfirmation: Bool public var allowsInspectableWebViews: Bool public var enablesVerboseWebViewLogging: Bool public init( fontSize: CGFloat = 15, lineHeightMultiple: CGFloat = 1.6, fontChoice: RDEPUBReaderFontChoice = .system, numberOfColumns: Int = 1, columnGap: CGFloat = 20, displayType: RDReaderView.DisplayType = .pageCurl, landscapeDualPageEnabled: Bool = true, showsTableOfContents: Bool = true, allowsHighlights: Bool = true, showsSettingsPanel: Bool = true, reflowableContentInsets: UIEdgeInsets = UIEdgeInsets(top: 40, left: 16, bottom: 40, right: 16), fixedContentInset: UIEdgeInsets = .zero, theme: RDEPUBReaderTheme = .light, darkImageAdjustmentEnabled: Bool = true, darkImageBlendRatio: CGFloat = 0.15, fixedLayoutFit: RDEPUBFixedLayoutFit = .page, fixedLayoutSpreadMode: RDEPUBFixedLayoutSpreadMode = .automatic, textRenderingEngine: RDEPUBTextRenderingEngine = .dtCoreText, onDemandChapterWindowSize: Int = 3, metadataParsingConcurrency: Int = ProcessInfo.processInfo.activeProcessorCount, jumpSessionPolicy: RDEPUBJumpSessionPolicy = .default, allowedExternalURLSchemes: Set = ["https"], requiresExternalLinkConfirmation: Bool = true, allowsInspectableWebViews: Bool = false, enablesVerboseWebViewLogging: Bool = false ) { self.fontSize = fontSize self.lineHeightMultiple = lineHeightMultiple self.fontChoice = fontChoice self.numberOfColumns = max(1, numberOfColumns) self.columnGap = max(0, columnGap) self.displayType = displayType self.landscapeDualPageEnabled = landscapeDualPageEnabled self.showsTableOfContents = showsTableOfContents self.allowsHighlights = allowsHighlights self.showsSettingsPanel = showsSettingsPanel self.reflowableContentInsets = reflowableContentInsets self.fixedContentInset = fixedContentInset self.theme = theme self.darkImageAdjustmentEnabled = darkImageAdjustmentEnabled self.darkImageBlendRatio = max(0, min(0.35, darkImageBlendRatio)) self.fixedLayoutFit = fixedLayoutFit self.fixedLayoutSpreadMode = fixedLayoutSpreadMode self.textRenderingEngine = textRenderingEngine self.onDemandChapterWindowSize = Self.normalizedChapterWindowSize(onDemandChapterWindowSize) self.metadataParsingConcurrency = max(1, metadataParsingConcurrency) self.jumpSessionPolicy = jumpSessionPolicy self.allowedExternalURLSchemes = allowedExternalURLSchemes self.requiresExternalLinkConfirmation = requiresExternalLinkConfirmation self.allowsInspectableWebViews = allowsInspectableWebViews self.enablesVerboseWebViewLogging = enablesVerboseWebViewLogging } public static let `default` = RDEPUBReaderConfiguration() } extension RDEPUBReaderConfiguration { static func normalizedChapterWindowSize(_ size: Int) -> Int { let clamped = max(3, min(15, size)) return clamped % 2 == 0 ? clamped + 1 : clamped } var chapterWindowRadius: Int { onDemandChapterWindowSize / 2 } } extension RDEPUBReaderConfiguration { func makePreferences() -> RDEPUBPreferences { RDEPUBPreferences( fontSize: fontSize, lineHeightMultiple: lineHeightMultiple, reflowableContentInsets: reflowableContentInsets, fixedContentInset: fixedContentInset, numberOfColumns: numberOfColumns, columnGap: columnGap, themeBackgroundColor: theme.themeBackgroundColorCSS, themeTextColor: theme.themeTextColorCSS, fixedBackgroundColor: theme.themeBackgroundColorCSS, fixedLayoutFit: fixedLayoutFit, fixedLayoutSpreadMode: fixedLayoutSpreadMode ) } }