ReadViewSDK/Sources/RDReaderView/EPUBTextRendering/RDEPUBTextRenderer.swift

183 lines
5.4 KiB
Swift

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 富文本导入失败"
}
}
}