import UIKit public extension NSAttributedString.Key { static let rdPageBlockRange = NSAttributedString.Key("com.rdreader.epub.pageBlockRange") static let rdPageBlockIndex = NSAttributedString.Key("com.rdreader.epub.pageBlockIndex") static let rdPageFragmentID = NSAttributedString.Key("com.rdreader.epub.pageFragmentID") static let rdPageAttachmentKind = NSAttributedString.Key("com.rdreader.epub.pageAttachmentKind") } public struct RDEPUBTextRenderStyle { public var font: UIFont public var lineSpacing: CGFloat public var textColor: UIColor? public var backgroundColor: UIColor? public init(font: UIFont, lineSpacing: CGFloat, textColor: UIColor? = nil, backgroundColor: UIColor? = nil) { self.font = font self.lineSpacing = lineSpacing self.textColor = textColor self.backgroundColor = backgroundColor } } public enum RDEPUBTextStyleSheetLayerKind: String, CaseIterable, Equatable { case `default` case replace case dark case epub case user } public struct RDEPUBTextStyleSheetLayer: Equatable { public var kind: RDEPUBTextStyleSheetLayerKind public var css: String public init(kind: RDEPUBTextStyleSheetLayerKind, css: String) { self.kind = kind self.css = css } } public struct RDEPUBTextStyleSheetPackage: Equatable { public var layers: [RDEPUBTextStyleSheetLayer] public init(layers: [RDEPUBTextStyleSheetLayer]) { self.layers = layers } public var combinedCSS: String { layers .filter { !$0.css.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty } .map { layer in "/* \(layer.kind.rawValue) */\n\(layer.css)" } .joined(separator: "\n\n") } } public enum RDEPUBTextResourceReferenceKind: String, Equatable { case stylesheet case image } public struct RDEPUBTextResourceReferenceDiagnostic: Equatable { public var kind: RDEPUBTextResourceReferenceKind public var chapterHref: String public var originalReference: String public var normalizedHref: String? public var resolvedFileURL: URL? public var existsOnDisk: Bool public init( kind: RDEPUBTextResourceReferenceKind, chapterHref: String, originalReference: String, normalizedHref: String?, resolvedFileURL: URL?, existsOnDisk: Bool ) { self.kind = kind self.chapterHref = chapterHref self.originalReference = originalReference self.normalizedHref = normalizedHref self.resolvedFileURL = resolvedFileURL self.existsOnDisk = existsOnDisk } } public struct RDEPUBTextChapterContext: Equatable { public var href: String public var title: String public var html: String public var baseURL: URL? public var stylesheet: RDEPUBTextStyleSheetPackage public var resourceDiagnostics: [RDEPUBTextResourceReferenceDiagnostic] public init( href: String, title: String, html: String, baseURL: URL?, stylesheet: RDEPUBTextStyleSheetPackage, resourceDiagnostics: [RDEPUBTextResourceReferenceDiagnostic] ) { self.href = href self.title = title self.html = html self.baseURL = baseURL self.stylesheet = stylesheet self.resourceDiagnostics = resourceDiagnostics } } public struct RDEPUBTextChapterRenderRequest { public var context: RDEPUBTextChapterContext public var style: RDEPUBTextRenderStyle public init(context: RDEPUBTextChapterContext, style: RDEPUBTextRenderStyle) { self.context = context self.style = style } } public struct RDEPUBRenderedChapterContent { public var attributedString: NSAttributedString public var fragmentOffsets: [String: Int] public var resourceDiagnostics: [RDEPUBTextResourceReferenceDiagnostic] public init( attributedString: NSAttributedString, fragmentOffsets: [String: Int], resourceDiagnostics: [RDEPUBTextResourceReferenceDiagnostic] = [] ) { self.attributedString = attributedString self.fragmentOffsets = fragmentOffsets self.resourceDiagnostics = resourceDiagnostics } } public protocol RDEPUBTextRenderer { func renderChapter( request: RDEPUBTextChapterRenderRequest ) throws -> RDEPUBRenderedChapterContent func renderChapter( html: String, baseURL: URL?, style: RDEPUBTextRenderStyle ) throws -> RDEPUBRenderedChapterContent } public extension RDEPUBTextRenderer { func renderChapter( html: String, baseURL: URL?, style: RDEPUBTextRenderStyle ) throws -> RDEPUBRenderedChapterContent { let context = RDEPUBTextChapterContext( href: "", title: "", html: html, baseURL: baseURL, stylesheet: RDEPUBTextStyleSheetPackage(layers: []), resourceDiagnostics: [] ) return try renderChapter(request: RDEPUBTextChapterRenderRequest(context: context, style: style)) } } public enum RDEPUBTextRenderingError: LocalizedError { case htmlEncodingFailed case htmlImportFailed public var errorDescription: String? { switch self { case .htmlEncodingFailed: return "HTML 编码失败" case .htmlImportFailed: return "HTML 富文本导入失败" } } }