feat(epub): complete wxread parity scope

This commit is contained in:
shen
2026-05-26 09:19:37 +08:00
parent 23182e8b5a
commit 0e7d952fe5
31 changed files with 2619 additions and 1533 deletions
@@ -1,4 +1,5 @@
import UIKit
import CoreText
#if canImport(DTCoreText)
import DTCoreText
@@ -27,6 +28,8 @@ enum RDEPUBTextRendererSupport {
private static var didLogFootnoteAttachment = false
///
private static var didLogCoverAttachment = false
/// CTFontManager
private static var registeredFontPaths = Set<String>()
// MARK: - Fragment /
@@ -209,6 +212,11 @@ enum RDEPUBTextRendererSupport {
contentLanguageCode: contentLanguageCode,
sourceHTML: rawHTML
)
registerEmbeddedFonts(
in: stylesheetHrefReplacements.inlinedCSS + "\n" + inlineStyleCSS(in: normalizedHTML),
chapterHref: href,
resourceResolver: resourceResolver
)
let htmlWithBase = injectBaseHref(into: stylesheetHrefReplacements.html, baseURL: baseURL)
let htmlWithDefaultLayers = injectStyleTag(
into: htmlWithBase,
@@ -327,6 +335,58 @@ enum RDEPUBTextRendererSupport {
return RDEPUBAssetRepository.string(for: asset)
}
private static func inlineStyleCSS(in html: String) -> String {
guard let regex = try? NSRegularExpression(pattern: #"<style\b[^>]*>([\s\S]*?)</style>"#, options: [.caseInsensitive]) else {
return ""
}
let nsHTML = html as NSString
return regex.matches(in: html, range: NSRange(location: 0, length: nsHTML.length))
.compactMap { match in
guard match.numberOfRanges > 1 else { return nil }
return nsHTML.substring(with: match.range(at: 1))
}
.joined(separator: "\n")
}
private static func registerEmbeddedFonts(
in css: String,
chapterHref: String,
resourceResolver: RDEPUBResourceResolver?
) {
guard let resourceResolver,
let faceRegex = try? NSRegularExpression(pattern: #"@font-face\s*\{([\s\S]*?)\}"#, options: [.caseInsensitive]),
let urlRegex = try? NSRegularExpression(pattern: #"url\(([^)]+)\)"#, options: [.caseInsensitive]) else {
return
}
let nsCSS = css as NSString
for faceMatch in faceRegex.matches(in: css, range: NSRange(location: 0, length: nsCSS.length)) {
guard faceMatch.numberOfRanges > 1 else { continue }
let block = nsCSS.substring(with: faceMatch.range(at: 1))
let nsBlock = block as NSString
for urlMatch in urlRegex.matches(in: block, range: NSRange(location: 0, length: nsBlock.length)) {
guard urlMatch.numberOfRanges > 1 else { continue }
let rawReference = nsBlock.substring(with: urlMatch.range(at: 1))
.trimmingCharacters(in: CharacterSet(charactersIn: "\"' \n\r\t"))
guard !rawReference.isEmpty,
!rawReference.hasPrefix("data:"),
!rawReference.hasPrefix("http://"),
!rawReference.hasPrefix("https://"),
let fileURL = resourceResolver.fileURL(forReference: rawReference, relativeToHref: chapterHref) else {
continue
}
registerFontIfNeeded(at: fileURL)
}
}
}
private static func registerFontIfNeeded(at fileURL: URL) {
let standardizedPath = fileURL.standardizedFileURL.path
guard !registeredFontPaths.contains(standardizedPath) else { return }
CTFontManagerRegisterFontsForURL(fileURL as CFURL, .process, nil)
registeredFontPaths.insert(standardizedPath)
}
private static func normalizeAttachmentHTMLMarkers(in html: String) -> String {
var normalized = html