Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
222 changes: 115 additions & 107 deletions Aztec.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

28 changes: 0 additions & 28 deletions Aztec/Classes/Converters/HTMLToAttributedString.swift

This file was deleted.

16 changes: 16 additions & 0 deletions Aztec/Classes/Extensions/NSAttributedString+HTMLInitializer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Foundation
import UIKit

extension NSAttributedString {

convenience init(withHTML html: String, usingDefaultFontDescriptor descriptor: UIFontDescriptor) {

let htmlParser = HTMLParser()
let rootNode = htmlParser.parse(html)

let serializer = AttributedStringSerializer(usingDefaultFontDescriptor: descriptor)
let attributedString = serializer.serialize(rootNode)

self.init(attributedString: attributedString)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ import Foundation
import libxml2


class InHTMLConverter: SafeConverter {
class HTMLParser {

enum Error: String, Swift.Error {
case NoRootNode = "No root node"
}

/// Converts HTML data into an HTML Node representing the same data.
/// Parses HTML data into an HTML Node representing the same data.
///
/// - Parameters:
/// - html: the HTML string to convert.
/// - html: the HTML string to parse.
///
/// - Returns: the HTML root node.
///
func convert(_ html: String) -> RootNode {
func parse(_ html: String) -> RootNode {

// We wrap the HTML into a special root node, since it helps avoid conversion issues
// with libxml2, where the library would add custom tags to "fix" the HTML code we
Expand Down Expand Up @@ -69,7 +69,13 @@ class InHTMLConverter: SafeConverter {

guard let rootNode = rootNodePtr?.pointee,
let node = nodeConverter.convert(rootNode) as? RootNode else {
return RootNode(children: [])
return RootNode(children: [TextNode(text: "")])
}

// Don't let this method return an empty root node.
//
if node.children.count == 0 {
node.children.append(TextNode(text: html))
}

return node
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import Foundation
import libxml2


// MARK: - HTML Prettifier!
//
class OutHTMLConverter: Converter {
/// Composes the provided nodes into its HTML representation.
///
class HTMLSerializer {

/// Indentation Spaces to be applied
///
Expand All @@ -14,7 +14,6 @@ class OutHTMLConverter: Converter {
///
let prettyPrint: Bool


/// Default Initializer
///
/// - Parameters:
Expand All @@ -27,72 +26,69 @@ class OutHTMLConverter: Converter {
}


/// Converts a Node into it's HTML String Representation
/// Serializes a node into its HTML representation
///
func convert(_ rawNode: Node) -> String {
return convert(node: rawNode).trimmingCharacters(in: CharacterSet.newlines)
func serialize(_ node: Node) -> String {
return serialize(node: node).trimmingCharacters(in: CharacterSet.newlines)
}
}


// MARK: - Nodes: Serialization
// MARK: - Nodes: Composition
//
private extension OutHTMLConverter {
private extension HTMLSerializer {

/// Serializes a Node into it's HTML String Representation
/// Serializes a node into its HTML representation
///
func convert(node: Node, level: Int = 0) -> String {
func serialize(node: Node, level: Int = 0) -> String {
switch node {
case let node as RootNode:
return convert(root: node)
return serialize(root: node)
case let node as CommentNode:
return convert(comment: node)
return serialize(comment: node)
case let node as ElementNode:
return convert(element: node, level: level)
return serialize(element: node, level: level)
case let node as TextNode:
return convert(text: node)
return serialize(text: node)
default:
fatalError("We're missing support for a node type. This should not happen.")
}
}


/// Serializes a RootNode into it's HTML String Representation
/// Serializes a `RootNode` into its HTML representation
///
private func convert(root node: RootNode) -> String {
private func serialize(root node: RootNode) -> String {
return node.children.reduce("") { (result, node) in
return result + convert(node: node)
return result + serialize(node: node)
}
}


/// Serializes a CommentNode into it's HTML String Representation
/// Serializes a `CommentNode` into its HTML representation
///
private func convert(comment node: CommentNode) -> String {
private func serialize(comment node: CommentNode) -> String {
return "<!--" + node.comment + "-->"
}


/// Serializes an ElementNode into it's HTML String Representation
/// Serializes an `ElementNode` into its HTML representation
///
private func convert(element node: ElementNode, level: Int) -> String {
private func serialize(element node: ElementNode, level: Int) -> String {
let opening = openingTag(for: node, at: level)

guard let closing = closingTag(for: node, at: level) else {
return opening
}

let children = node.children.reduce("") { (html, child)in
return html + convert(node: child, level: level + 1)
return html + serialize(node: child, level: level + 1)
}

return opening + children + closing
}


/// Serializes a TextNode into it's HTML String Representation
/// Serializes an `TextNode` into its HTML representation
///
private func convert(text node: TextNode) -> String {
private func serialize(text node: TextNode) -> String {
return node.text().escapeHtmlNamedEntities()
}
}
Expand All @@ -101,13 +97,13 @@ private extension OutHTMLConverter {

// MARK: - ElementNode: Helpers
//
private extension OutHTMLConverter {
private extension HTMLSerializer {

/// Returns the Opening Tag for a given Element Node
///
func openingTag(for node: ElementNode, at level: Int) -> String {
let prefix = requiresOpeningTagPrefix(node) ? prefixForTag(at: level) : ""
let attributes = convert(attributes: node.attributes)
let attributes = serialize(attributes: node.attributes)

return prefix + "<" + node.name + attributes + ">"
}
Expand Down Expand Up @@ -184,11 +180,11 @@ private extension OutHTMLConverter {

// MARK: - Attributes: Serialization
//
private extension OutHTMLConverter {
private extension HTMLSerializer {

/// Serializes a collection of Attributes into their HTML Form
/// Serializes an array of attributes into their HTML representation
///
func convert(attributes: [Attribute]) -> String {
func serialize(attributes: [Attribute]) -> String {
return attributes.reduce("") { (html, attribute) in
return html + String(.space) + attribute.toString()
}
Expand All @@ -199,7 +195,7 @@ private extension OutHTMLConverter {

// MARK: - Private Constants
//
private extension OutHTMLConverter {
private extension HTMLSerializer {

struct Constants {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@ import Foundation
import UIKit
import libxml2

/// Parses an attributed string into an HTML tree.
///
class AttributedStringParser {

// MARK: - NSAttributedStringToNodes
//
class NSAttributedStringToNodes: Converter {

/// Converts an Attributed String Instance into it's HTML Tree Representation.
/// Parses an attributed string and returns the corresponding HTML tree.
///
/// - Parameters:
/// - attrString: Attributed String that should be converted.
/// - attrString: the attributed string to parse
///
/// - Returns: RootNode, representing the DOM Tree.
/// - Returns: the HTML tree.
///
func convert(_ attrString: NSAttributedString) -> RootNode {
func parse(_ attrString: NSAttributedString) -> RootNode {
var nodes = [Node]()
var previous: [Node]?

Expand Down Expand Up @@ -112,7 +111,7 @@ class NSAttributedStringToNodes: Converter {

// MARK: - Merge: Helpers
//
private extension NSAttributedStringToNodes {
private extension AttributedStringParser {

/// Defines a Tree Branch: Collection of Nodes, with a set of Leaves
///
Expand Down Expand Up @@ -161,7 +160,7 @@ private extension NSAttributedStringToNodes {

// MARK: - Merge: Styles
//
private extension NSAttributedStringToNodes {
private extension AttributedStringParser {

/// Given a collection of branches, this method will iterate branch by branch and will:
///
Expand Down Expand Up @@ -309,7 +308,7 @@ private extension NSAttributedStringToNodes {

// MARK: - Merge: Paragraphs
//
private extension NSAttributedStringToNodes {
private extension AttributedStringParser {

/// Attempts to merge the Right array of Element Nodes (Paragraph Level) into the Left array of Nodes.
///
Expand Down Expand Up @@ -379,7 +378,7 @@ private extension NSAttributedStringToNodes {

// MARK: - Paragraph Nodes Extraction
//
extension NSAttributedStringToNodes {
extension AttributedStringParser {

/// Returns the "Rightmost" Blocklevel Node from a collection fo nodes.
///
Expand Down Expand Up @@ -422,7 +421,7 @@ extension NSAttributedStringToNodes {

// MARK: - Paragraph Nodes: Alloc'ation
//
private extension NSAttributedStringToNodes {
private extension AttributedStringParser {

/// Extracts the ElementNodes contained within a Paragraph's AttributedString.
///
Expand Down Expand Up @@ -619,7 +618,7 @@ private extension NSAttributedStringToNodes {

// MARK: - Style Nodes: Alloc'ation
//
private extension NSAttributedStringToNodes {
private extension AttributedStringParser {

/// Extracts all of the Style Nodes contained within a collection of AttributedString Attributes.
///
Expand Down Expand Up @@ -771,9 +770,9 @@ private extension NSAttributedStringToNodes {
}


// MARK: - Leaf Nodes: Alloc'ation
// MARK: - Leaf Nodes: Allocation
//
private extension NSAttributedStringToNodes {
private extension AttributedStringParser {

/// Extract all of the Leaf Nodes contained within an Attributed String. We consider the following as Leaf:
/// Plain Text, Attachments of any kind [Line, Comment, HTML, Image].
Expand Down Expand Up @@ -847,9 +846,9 @@ private extension NSAttributedStringToNodes {
return []
}

let converter = InHTMLConverter()
let htmlParser = HTMLParser()

let rootNode = converter.convert(attachment.rawHTML)
let rootNode = htmlParser.parse(attachment.rawHTML)

guard let firstChild = rootNode.children.first else {
return processTextNodes(from: attachment.rawHTML)
Expand Down
Loading