// Copyright 2017 Yandex LLC. All rights reserved.
import Foundation
import BaseUI
public final class HTMLParser {
private let resolver = HTMLEntitiesResolver()
private let tagsToReplace: [String: String]
public init(tagsToReplace: [String: String]) {
self.tagsToReplace = tagsToReplace
}
public convenience init() {
self.init(tagsToReplace: defaultTagsToReplace)
}
public func parse(html: String, with baseTypo: Typo) -> NSAttributedString? {
let stringWithReplacedSymbols = replaceSymbols(in: html)
let stringWithResolvedHTMLEntities = resolver.resolveHTMLEntities(in: stringWithReplacedSymbols)
let tagWrappedString = "\(stringWithResolvedHTMLEntities)"
guard let data = tagWrappedString.data(using: .utf16) else {
return nil
}
guard let taggedStrings = parse(htmlData: data) else {
return nil
}
let resultString = NSMutableAttributedString()
taggedStrings.forEach { substring in
resultString.append(substring.attributedString(with: baseTypo))
}
return resultString
}
private func parse(htmlData: Data) -> [HTMLTaggedString]? {
let parser = XMLParser(data: htmlData)
parser.externalEntityResolvingPolicy = .never
let delegate = HTMLParserDelegate()
parser.delegate = delegate
let success = parser.parse()
let result: [HTMLTaggedString]? = success ? delegate.taggedStrings : nil
parser.delegate = nil
return result
}
private func replaceSymbols(in string: String) -> String {
var resultString = string
tagsToReplace.forEach { symbol in
resultString = resultString.replacingOccurrences(
of: symbol.key,
with: symbol.value,
options: [.literal, .caseInsensitive],
range: nil
)
}
return resultString
}
}
private let defaultTagsToReplace: [String: String] = [
"
": "\n",
"
": "\n",
]