90 lines
3.7 KiB
Swift
90 lines
3.7 KiB
Swift
//
|
|
// Copyright 2024 New Vector Ltd.
|
|
// Copyright 2020 The Matrix.org Foundation C.I.C
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
// Please see LICENSE in the repository root for full details.
|
|
//
|
|
|
|
import DTCoreText
|
|
|
|
public extension DTHTMLElement {
|
|
typealias ImageHandler = (_ sourceURL: String, _ width: CGFloat, _ height: CGFloat) -> URL?
|
|
|
|
/// Sanitize the element using the given parameters.
|
|
/// - Parameters:
|
|
/// - allowedHTMLTags: An array of tags that are allowed. All other tags will be removed.
|
|
/// - font: The default font to use when resetting the content of any unsupported tags.
|
|
/// - imageHandler: An optional image handler to be run on `img` tags (if allowed) to update the `src` attribute.
|
|
@objc func sanitize(with allowedHTMLTags: [String], bodyFont font: UIFont, imageHandler: ImageHandler?) {
|
|
if let name = name, !allowedHTMLTags.contains(name) {
|
|
|
|
// This is an unsupported tag.
|
|
// Remove any attachments to fix rendering.
|
|
textAttachment = nil
|
|
|
|
// If the element has plain text content show that,
|
|
// otherwise prevent the tag from displaying.
|
|
if let stringContent = attributedString()?.string,
|
|
!stringContent.isEmpty,
|
|
let element = DTTextHTMLElement(name: nil, attributes: nil) {
|
|
element.setText(stringContent)
|
|
removeAllChildNodes()
|
|
addChildNode(element)
|
|
|
|
if let parent = parent() {
|
|
element.inheritAttributes(from: parent)
|
|
} else {
|
|
fontDescriptor = DTCoreTextFontDescriptor()
|
|
fontDescriptor.fontFamily = font.familyName
|
|
fontDescriptor.fontName = font.fontName
|
|
fontDescriptor.pointSize = font.pointSize
|
|
paragraphStyle = DTCoreTextParagraphStyle.default()
|
|
|
|
element.inheritAttributes(from: self)
|
|
}
|
|
element.interpretAttributes()
|
|
|
|
} else if let parent = parent() {
|
|
parent.removeChildNode(self)
|
|
} else {
|
|
didOutput = true
|
|
}
|
|
|
|
} else {
|
|
// Process images with the handler when self is an image tag.
|
|
if name == "img", let imageHandler = imageHandler {
|
|
process(with: imageHandler)
|
|
}
|
|
|
|
// This element is a supported tag, but it may contain children that aren't,
|
|
// so santize all child nodes to ensure correct tags.
|
|
if let childNodes = childNodes as? [DTHTMLElement] {
|
|
childNodes.forEach { $0.sanitize(with: allowedHTMLTags, bodyFont: font, imageHandler: imageHandler) }
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Process the element with the supplied image handler.
|
|
private func process(with imageHandler: ImageHandler) {
|
|
// Get the values required to pass to the image handler
|
|
guard let sourceURL = attributes["src"] as? String else { return }
|
|
|
|
var width: CGFloat = -1
|
|
if let widthString = attributes["width"] as? String,
|
|
let widthDouble = Double(widthString) {
|
|
width = CGFloat(widthDouble)
|
|
}
|
|
|
|
var height: CGFloat = -1
|
|
if let heightString = attributes["height"] as? String,
|
|
let heightDouble = Double(heightString) {
|
|
height = CGFloat(heightDouble)
|
|
}
|
|
|
|
// If the handler returns an updated URL, update the text attachment.
|
|
guard let localSourceURL = imageHandler(sourceURL, width, height) else { return }
|
|
textAttachment.contentURL = localSourceURL
|
|
}
|
|
}
|