// RDEPUBParser+ReadingProfile.swift // EPUB 阅读配置文件判断 // 根据 metadata 的 layout 属性和 manifest/spine 内容, // 将出版物分类为 webFixedLayout(固定版式)、webInteractive(含脚本/多媒体)或 textReflowable(纯文本重排)。 import Foundation extension RDEPUBParser { /// 判断出版物的阅读配置文件 /// 优先判断固定版式,其次检测交互内容(脚本/iframe/视频等),默认为文本重排 public func readingProfile() -> RDEPUBReadingProfile { if metadata.layout == .fixed { return .webFixedLayout } return hasInteractiveContent() ? .webInteractive : .textReflowable } /// 检测出版物是否包含交互内容 /// 通过 manifest 中的脚本媒体类型和 "scripted" 属性, /// 以及扫描 HTML 中的 script/iframe/video/audio/canvas/svg/form 等标签 public func hasInteractiveContent() -> Bool { let interactiveMediaTypes = [ "application/javascript", "text/javascript", "application/ecmascript" ] if manifest.values.contains(where: { item in interactiveMediaTypes.contains(item.mediaType.lowercased()) || item.properties.contains("scripted") }) { return true } let interactivePattern = #"<(script|iframe|video|audio|canvas|svg|form)\b|\bon(load|click|touchstart|touchend|mouseover)=|hype_generated_script|swiper|webview"# for item in spine where item.linear && (item.mediaType.contains("html") || item.mediaType.contains("xhtml")) { guard let html = htmlString(forRelativePath: item.href) else { continue } if html.range(of: interactivePattern, options: [.regularExpression, .caseInsensitive]) != nil { return true } } return false } }