ReadViewSDK/Sources/RDReaderView/EPUBCore/RDEPUBModels.swift
shenlei c65c190b71 feat: EPUB阅读器搜索、注释、CFI模块及大书远距跳转优化
- 实现EPUB阅读器搜索功能及选中注释功能
- 优化CFI模块,修复代码审查发现的11个问题
- 实现大书远距目录跳转与后台补全优化方案
- 优化设置面板与章节运行时联动
- 重构及大量改进优化
2026-06-22 20:26:34 +08:00

201 lines
4.5 KiB
Swift

import Foundation
public enum RDEPUBLayout: String, Codable {
case reflowable
case fixed
}
public enum RDEPUBReadingProfile: String, Codable {
case webInteractive
case webFixedLayout
case textReflowable
}
public enum RDEPUBReadingProgression: String, Codable {
case ltr
case rtl
case auto
}
public enum RDEPUBPageSpread: String, Codable {
case left
case right
case center
}
public struct RDEPUBMetadata: Codable, Equatable {
public var identifier: String?
public var title: String
public var author: String?
public var language: String?
public var version: String?
public var layout: RDEPUBLayout
public var spread: String?
public var readingProgression: RDEPUBReadingProgression
public init(
identifier: String? = nil,
title: String = "",
author: String? = nil,
language: String? = nil,
version: String? = nil,
layout: RDEPUBLayout = .reflowable,
spread: String? = nil,
readingProgression: RDEPUBReadingProgression = .auto
) {
self.identifier = identifier
self.title = title
self.author = author
self.language = language
self.version = version
self.layout = layout
self.spread = spread
self.readingProgression = readingProgression
}
}
public struct RDEPUBManifestItem: Codable, Equatable {
public var id: String
public var href: String
public var mediaType: String
public var properties: [String]
public var fallback: String?
public var mediaOverlay: String?
public var title: String?
public init(
id: String,
href: String,
mediaType: String,
properties: [String] = [],
fallback: String? = nil,
mediaOverlay: String? = nil,
title: String? = nil
) {
self.id = id
self.href = href
self.mediaType = mediaType
self.properties = properties
self.fallback = fallback
self.mediaOverlay = mediaOverlay
self.title = title
}
public var isNavigationDocument: Bool {
properties.contains("nav")
}
public var isNCX: Bool {
mediaType == "application/x-dtbncx+xml"
}
}
public struct RDEPUBSpineItem: Codable, Equatable {
public var idref: String
public var href: String
public var mediaType: String
public var title: String
public var linear: Bool
public var properties: [String]
public var pageSpread: RDEPUBPageSpread?
public var layout: RDEPUBLayout?
public init(
idref: String,
href: String,
mediaType: String,
title: String,
linear: Bool = true,
properties: [String] = [],
pageSpread: RDEPUBPageSpread? = nil,
layout: RDEPUBLayout? = nil
) {
self.idref = idref
self.href = href
self.mediaType = mediaType
self.title = title
self.linear = linear
self.properties = properties
self.pageSpread = pageSpread
self.layout = layout
}
}
public struct EPUBTableOfContentsItem: Codable, Equatable {
public var title: String
public var href: String
public var children: [EPUBTableOfContentsItem]
public init(title: String, href: String, children: [EPUBTableOfContentsItem] = []) {
self.title = title
self.href = href
self.children = children
}
}
public enum RDEPUBParserError: LocalizedError {
case archiveOpenFailed(URL)
case missingContainerXML
case missingRootFile
case invalidRootFilePath(String)
case invalidXML(URL)
case missingManifestItem(idref: String)
case emptySpine
case invalidArchiveEntryPath(String)
public var errorDescription: String? {
switch self {
case .archiveOpenFailed(let url):
return "无法打开 EPUB 压缩包: \(url.lastPathComponent)"
case .missingContainerXML:
return "EPUB 缺少 META-INF/container.xml"
case .missingRootFile:
return "container.xml 中未找到 rootfile"
case .invalidRootFilePath(let path):
return "OPF 路径无效: \(path)"
case .invalidXML(let url):
return "XML 解析失败: \(url.lastPathComponent)"
case .missingManifestItem(let idref):
return "spine itemref 找不到对应 manifest 项: \(idref)"
case .emptySpine:
return "OPF spine 为空"
case .invalidArchiveEntryPath(let path):
return "归档条目路径非法: \(path)"
}
}
}