Skip to content

Commit 8d4ecd6

Browse files
Merge pull request #1310 from wordpress-mobile/issue/add_architecture_documentation
Add architecture documentation.
2 parents dbe6a0a + 05bbee7 commit 8d4ecd6

File tree

4 files changed

+103
-0
lines changed

4 files changed

+103
-0
lines changed

Aztec.xcodeproj/project.pbxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,7 @@
541541
FFD3C1702344DB4E00AE8DA0 /* ForegroundColorCSSAttributeMatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForegroundColorCSSAttributeMatcher.swift; sourceTree = "<group>"; };
542542
FFD3C1722344DCA900AE8DA0 /* ForegroundColorElementAttributeConverter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForegroundColorElementAttributeConverter.swift; sourceTree = "<group>"; };
543543
FFD436971E3180A500A0E26F /* FontFormatterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FontFormatterTests.swift; sourceTree = "<group>"; };
544+
FFE859252564232100FAEB12 /* Architecture.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Architecture.md; sourceTree = "<group>"; };
544545
FFFA53E423C6277700829A43 /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = "<group>"; };
545546
FFFA53E623C628E700829A43 /* BoldFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoldFormatter.swift; sourceTree = "<group>"; };
546547
FFFEC7DA1EA7698900F4210F /* VideoAttachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoAttachment.swift; sourceTree = "<group>"; };
@@ -1397,6 +1398,7 @@
13971398
isa = PBXGroup;
13981399
children = (
13991400
FF5DCBF1248ED900006D9FC7 /* ReleaseProcess.md */,
1401+
FFE859252564232100FAEB12 /* Architecture.md */,
14001402
);
14011403
path = Documentation;
14021404
sourceTree = "<group>";

Documentation/Architecture.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Introduction
2+
3+
Aztec provides a UI component to display and edit HTML content in a performant way. It does not implement its text layout functionality. Instead, it builds on top of Apple's [UITextView](https://developer.apple.com/documentation/uikit/uitextview) provided by the UIKit framework.
4+
5+
The library is composed of two main systems:
6+
- [TextView](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/TextKit/TextView.swift) a user interface component that allows the presentation and editing of the HTML
7+
- HTML converters that transform raw HTML to [NSAttributedString](https://developer.apple.com/documentation/foundation/nsattributedstring) and vice-versa
8+
9+
You can use each of the components separately, but they are all composed under the TextView class to provide a single element to display and edit HTML.
10+
11+
# TextView
12+
13+
[TextView](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/TextKit/TextView.swift) is the core class of the system. It's a `UITextView` subclass that implements custom subclasses of `NSTextStorage`, `NSLayoutManager`, and `NSAttributeString` attributes to allow HTML editing.
14+
15+
There are two main tasks that this class handles:
16+
17+
- Maintenance of the custom `NSAttributedString` attributes and attachments as the user edits the content to represent a valid HTML string.
18+
- Presentation of the custom attributes, like lists and quotes
19+
20+
The TextView and [TextStorage](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/TextKit/TextStorage.swift) classes are responsible for the maintenance of the `NSAttributedString`.
21+
22+
They handle user manipulation of formatting (strong, emphasis, lists, blockquotes), embeds (images, videos, separators), and the user interaction with them (line breaks, composition).
23+
24+
The formatting is implemented by the `toggle(formatter, atRange)` method in combination with AttributeFormatter objects. We split the formatter objects into the following categories:
25+
- [AttributeFormatter](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/Formatters/Base/AttributeFormatter.swift) that handle characters attributes like bold, italic,
26+
- [ParagraphAttributedFormatter](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/Formatters/Base/ParagraphAttributeFormatter.swift), that control paragraph attributes like lists, blockquotes, headings, etc.
27+
28+
For embeds, like images, videos, separators, tables, etc we use subclasses of the [NSTextAttachment](https://developer.apple.com/documentation/uikit/nstextattachment) object:
29+
- [MediaAttachment](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/TextKit/MediaAttachment.swift),
30+
- [RenderableAttachment](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/TextKit/RenderableAttachment.swift)
31+
32+
The `replace(at:NSRange, with: NSTextAttachment)` handles the insertion of embeds with multiple helpers methods to handle each specific type of embed.
33+
34+
The attachment objects work by delegating their presentation using the following protocols:
35+
- [TextViewAttachmentDelegate](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/TextKit/TextView.swift#L7): for media elements where the data can be be downloaded asynchronously from the web. For example `img` and `video` elements.
36+
- [TextViewAttachmentImageProvider](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/TextKit/TextView.swift#L75): for HTML elements, that have a static presentation like `HR`, HTML comments, `More`, `NextPage`.
37+
38+
Another essential task is interaction with user input and handling new lines, deleting list items, and indents around:
39+
- Lists
40+
- Quote
41+
- Paragraphs
42+
43+
The following "ensure..." methods handle all these scenarios:
44+
- ['ensureRemovalOfParagraphAttributesWhenPressingEnterInAnEmptyParagraph'](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/TextKit/TextView.swift#L1945)
45+
- [ensureInsertionOfEndOfLine](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/TextKit/TextView.swift#L697)
46+
- [evaluateRemovalOfSingleLineParagraphAttributesAfterSelectionChange](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/TextKit/TextView.swift#L1884)
47+
48+
These methods check if you are adding or removing new paragraphs around lists or quotes and update the attributes accordingly.
49+
50+
# Presentation of the custom attributes
51+
52+
The [LayoutManager](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/TextKit/LayoutManager.swift) class handles the presentation for the custom attributes that the standard [NSLayoutManager](https://developer.apple.com/documentation/uikit/nslayoutmanager) is unable to do:
53+
54+
- Backgrounds and vertical bars for Quotes: `drawBlockquotes`
55+
- Backgrounds for Pre: `drawHTMLPre`
56+
- List bullets: `drawLists`
57+
58+
The layout manager uses the extra information provided by the [ParagraphStyle](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/TextKit/ParagraphStyle.swift) class to be able to render the correct background and bullets for each element.
59+
60+
# HTML Converter
61+
62+
The [HTMLConverter](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/NSAttributedString/Conversions/HTMLConverter.swift) class handles all the conversation between HTML and NSAttributedString. The main entry points are:
63+
- [attributedString(from:html, defaultAttributes)](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/NSAttributedString/Conversions/HTMLConverter.swift#L58), converts from HTML to a NSAttributedString object
64+
- [html(from:NSAttributedString, pretify)](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/NSAttributedString/Conversions/HTMLConverter.swift#L110) converts from NSAttributedString to HTML
65+
66+
# Converting from HTML to NSAttributedString
67+
68+
Two classes are responsible for the conversion:
69+
- [HTMLParser](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/Libxml2/Converters/In/HTMLParser.swift) transforms from HTML text to an in-memory XML tree.
70+
- [AttributedStringSerializer](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/NSAttributedString/Conversions/AttributedStringSerializer.swift) converts from the in-memory tree to NSAttributedString.
71+
72+
<img src="resources/html_to_nsattributedstring.png">
73+
74+
The `HTMLParser` class uses the libXML2 library to read the raw HTML. It then transforms the document returned to a more developer-friendly DOM node tree.
75+
The DOM node tree implementation has classes representing elements, text, CSS attributes, CSS values, and comments.
76+
`AttributedStringSerializer` then converts this DOM tree to an NSAttributedString.
77+
78+
The specialized `NSAttributedString` has custom attributes that add extra information about the HTML elements that the string contains:
79+
- [HTMLRepresentation](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/NSAttributedString/Attributes/HTMLRepresentation.swift) stores the original HTML elements and attributes that were in the initial HTML
80+
- [ParagraphStyle](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/TextKit/ParagraphStyle.swift) is a subclass on [NSParagraphMutableStyle](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/TextKit/ParagraphStyle.swift), that allows representation of a hierarchy of elements, for example, nested lists, blockquote inside lists, and others.
81+
82+
Specialized [Converter](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/Converters/ElementsToAttributedString/Implementations/GenericElementConverter.swift) classes transform from HTML Elements to NSAttributedString text and attributes, for example:
83+
- [ImageElementConverter](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/Converters/ElementsToAttributedString/Implementations/ImageElementConverter.swift) converts `img `elements to a custom attachment,
84+
- [GenericElementConverter](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/Converters/ElementsToAttributedString/Implementations/GenericElementConverter.swift) transform `strong` elements to a font attribute
85+
- [LIElementConverter](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/Converters/ElementsToAttributedString/Implementations/GenericElementConverter.swift) transform `li` elements from a list to `ParagrahStyle`
86+
87+
# Converting NSAttributedString to HTML
88+
89+
In a mirror process from the HTML to NSAttributed conversion, we have the following classes:
90+
- [AttributedStringParser](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/NSAttributedString/Conversions/AttributedStringParser.swift) converts from the `NSAttributedString` to an in-memory tree
91+
- [HTMLSerializer](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/Libxml2/Converters/Out/HTMLSerializer.swift) transforms from the in-memory tree to an HTML string.
92+
93+
<img src="resources/nsattributedstring_to_html.png">
94+
95+
The `AttributedStringParser` transforms the `NSAttributedString` to a DOM tree. On a first pass, it iterates paragraph by paragraph and uses [StringAttributesConverters](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/Converters/ElementsToAttributedString/Implementations/GenericElementConverter.swift) and [AttachmentConverters](https://github.com/wordpress-mobile/AztecEditor-iOS/blob/develop/Aztec/Classes/Converters/ElementsToAttributedString/Implementations/GenericElementConverter.swift) to convert from `NSStringAttributes` and `NSAttachments` to the DOM.
96+
Then it goes through the DOM tree and merges nodes to create a simplified version of the tree.
97+
98+
We then use the `HTMLSerializer` to convert the DOM Tree to an HTML string.
99+
100+
101+
55.6 KB
Loading
36.5 KB
Loading

0 commit comments

Comments
 (0)