2022-12-03 23:58:19 +00:00
|
|
|
//
|
|
|
|
// HTMLConverter.swift
|
|
|
|
// Tusker
|
|
|
|
//
|
|
|
|
// Created by Shadowfacts on 12/3/22.
|
|
|
|
// Copyright © 2022 Shadowfacts. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import UIKit
|
2023-12-23 01:44:46 +00:00
|
|
|
import HTMLStreamer
|
2022-12-03 23:58:19 +00:00
|
|
|
import WebURL
|
|
|
|
import WebURLFoundationExtras
|
|
|
|
|
2023-12-23 01:44:46 +00:00
|
|
|
class HTMLConverter {
|
2022-12-03 23:58:19 +00:00
|
|
|
|
|
|
|
static let defaultFont = UIFont.systemFont(ofSize: 17)
|
2022-12-15 01:07:16 +00:00
|
|
|
static let defaultMonospaceFont = UIFont.monospacedSystemFont(ofSize: 17, weight: .regular)
|
2022-12-03 23:58:19 +00:00
|
|
|
static let defaultColor = UIColor.label
|
|
|
|
static let defaultParagraphStyle: NSParagraphStyle = {
|
|
|
|
let style = NSMutableParagraphStyle()
|
|
|
|
// 2 points is enough that it doesn't make things look weirdly spaced out, but leaves a slight gap when there are multiple lines of emojis
|
|
|
|
style.lineSpacing = 2
|
|
|
|
return style
|
|
|
|
}()
|
|
|
|
|
2024-02-23 04:53:27 +00:00
|
|
|
private let converter: AttributedStringConverter<Callbacks>
|
2022-12-03 23:58:19 +00:00
|
|
|
|
2023-12-23 01:44:46 +00:00
|
|
|
init(font: UIFont, monospaceFont: UIFont, color: UIColor, paragraphStyle: NSParagraphStyle) {
|
|
|
|
let config = AttributedStringConverterConfiguration(font: font, monospaceFont: monospaceFont, color: color, paragraphStyle: paragraphStyle)
|
|
|
|
self.converter = AttributedStringConverter(configuration: config)
|
|
|
|
}
|
|
|
|
|
2022-12-03 23:58:19 +00:00
|
|
|
func convert(_ html: String) -> NSAttributedString {
|
2023-12-23 01:44:46 +00:00
|
|
|
converter.convert(html: html)
|
2022-12-03 23:58:19 +00:00
|
|
|
}
|
2023-12-23 01:44:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
extension HTMLConverter {
|
2024-04-11 16:44:41 +00:00
|
|
|
// note: this is duplicated in NotificationExtension
|
2023-12-23 01:44:46 +00:00
|
|
|
struct Callbacks: HTMLConversionCallbacks {
|
|
|
|
static func makeURL(string: String) -> URL? {
|
|
|
|
// Converting WebURL to URL is a small but non-trivial expense (since it works by
|
|
|
|
// serializing the WebURL as a string and then having Foundation parse it again),
|
|
|
|
// so, if available, use the system parser which doesn't require another round trip.
|
|
|
|
if #available(iOS 16.0, macOS 13.0, *),
|
|
|
|
let url = try? URL.ParseStrategy().parse(string) {
|
|
|
|
url
|
|
|
|
} else if let web = WebURL(string),
|
|
|
|
let url = URL(web) {
|
|
|
|
url
|
2022-12-03 23:58:19 +00:00
|
|
|
} else {
|
2023-12-23 01:44:46 +00:00
|
|
|
URL(string: string)
|
2023-09-27 21:35:36 +00:00
|
|
|
}
|
2023-12-23 01:44:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static func elementAction(name: String, attributes: [Attribute]) -> ElementAction {
|
|
|
|
guard name == "span" else {
|
|
|
|
return .default
|
2022-12-03 23:58:19 +00:00
|
|
|
}
|
2023-12-23 01:44:46 +00:00
|
|
|
let clazz = attributes.attributeValue(for: "class")
|
|
|
|
if clazz == "invisible" {
|
|
|
|
return .skip
|
|
|
|
} else if clazz == "ellipsis" {
|
2024-01-17 00:17:44 +00:00
|
|
|
return .append("…")
|
2023-11-18 15:56:05 +00:00
|
|
|
} else {
|
2023-12-23 01:44:46 +00:00
|
|
|
return .default
|
2023-09-27 21:35:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|