Skip to content

Instantly share code, notes, and snippets.

@lalkrishna
Last active March 11, 2022 06:37
Show Gist options
  • Save lalkrishna/5157819288933eddf48465bb1a12d37c to your computer and use it in GitHub Desktop.
Save lalkrishna/5157819288933eddf48465bb1a12d37c to your computer and use it in GitHub Desktop.
Build AttributedString from HTML String content. Fonts can be linked locally.
//
// HTMLBuilder.swift
// HTMLBuilder
//
// Created by Lal Krishna on 11/03/22.
// Copyright © 2022 Lal Krishna. All rights reserved.
//
import Foundation
import UIKit
extension HTMLBuilder {
static func `default`() -> Self {
let font = UIFont.appFont(style: .regular, ofSize: 22)
let fontFaces = [
font.fontName: font.fontName + ".ttf"
]
let builder = HTMLBuilder(defaultFontName: font.fontName, fontFaces: fontFaces)
return builder
}
}
struct HTMLBuilder {
let css: String
typealias FontName = String
typealias FontFileName = String
/// Key-value pair
/// Example: `[ "Arial-Bold": "Arial_Bold.ttf"`
var fontFaces: [FontName: FontFileName]
private var bodyContent: String = ""
init(defaultFontName: String, fontFaces: [FontName: FontFileName]? = nil, css: String? = nil) {
self.fontFaces = fontFaces ?? [:]
self.css = css ?? Self.defaultCSS(defaultFontName: defaultFontName)
}
private var attributedText: NSAttributedString? {
let htmlContent = html
let htmlData = NSString(string: htmlContent).data(using: String.Encoding.unicode.rawValue)
let options = [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html]
let attributedString = try? NSAttributedString(data: htmlData!, options: options, documentAttributes: nil)
return attributedString
}
mutating func buildAttributedString(_ htmlContent: String, color: UIColor? = nil) -> NSAttributedString? {
self.bodyContent = htmlContent
guard let attributed = attributedText else { return nil }
guard let color = color else { return attributed }
let mutable = NSMutableAttributedString(attributedString: attributed)
mutable.addAttributes([.foregroundColor: color], range: .init(location: 0, length: attributed.length))
return mutable
}
var html: String {
"""
<html>
\(head)
<body>
\(bodyContent)
</body>
</html>
"""
}
var head: String {
"""
</head>
\(style)
</head>
"""
}
var style: String {
"""
<style>
\(fontFacesCSS)
\(css)
</style>
"""
}
private static func defaultCSS(defaultFontName: String) -> String {
"""
body {
font-family: '\(defaultFontName)';
line-height: 1.6;
}
"""
}
// var css = buildFontFaces()
// let linkCSS = #"<link rel="stylesheet" type="text/css" href="productDescriptionStyle.css">"#
var fontFacesCSS: String {
fontFaces.reduce("") { $0 + "\n" + Self.fontFace(name: $1.key, path: $1.value) }
}
mutating func addFontFace(name: String, path: String) {
fontFaces[name] = path
}
private static func fontFace(name: String, path: String) -> String {
"""
@font-face {
font-family: '\(name)';
src: local('\(name)'),url('\(path)')');
}
"""
}
}
// Usage
// HTMLBuilder.default().buildAttributedString(htmlContentString, color: UIColor.red)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment