Skip to content

Commit 26443eb

Browse files
Merge pull request #1283 from wordpress-mobile/release/1.19.0
Release/1.19.0
2 parents c6a8103 + fe24074 commit 26443eb

File tree

19 files changed

+258
-12
lines changed

19 files changed

+258
-12
lines changed

Aztec.xcodeproj/project.pbxproj

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,10 @@
239239
FF7A1C511E5651EA00C4C7C8 /* LineAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7A1C501E5651EA00C4C7C8 /* LineAttachment.swift */; };
240240
FF7C89B01E3BC52F000472A8 /* NSAttributedString+FontTraits.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7C89AF1E3BC52F000472A8 /* NSAttributedString+FontTraits.swift */; };
241241
FF7EAEC4234D253B007A26E0 /* FontProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7EAEC3234D253B007A26E0 /* FontProvider.swift */; };
242+
FF94935E245738AC0085ABB3 /* SuperscriptStringAttributeConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF94935D245738AC0085ABB3 /* SuperscriptStringAttributeConverter.swift */; };
243+
FF949360245740250085ABB3 /* SuperscriptFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF94935F245740250085ABB3 /* SuperscriptFormatter.swift */; };
244+
FF949362245744090085ABB3 /* SubscriptStringAttributeConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF949361245744090085ABB3 /* SubscriptStringAttributeConverter.swift */; };
245+
FF949364245744560085ABB3 /* SubscriptFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF949363245744560085ABB3 /* SubscriptFormatter.swift */; };
242246
FFA61E891DF18F3D00B71BF6 /* ParagraphStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA61E881DF18F3D00B71BF6 /* ParagraphStyle.swift */; };
243247
FFA61EC21DF6C1C900B71BF6 /* NSAttributedString+Archive.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA61EC11DF6C1C900B71BF6 /* NSAttributedString+Archive.swift */; };
244248
FFB5D29720BEB21A0038DCFB /* CiteFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB5D29620BEB21A0038DCFB /* CiteFormatter.swift */; };
@@ -523,6 +527,10 @@
523527
FF7A1C501E5651EA00C4C7C8 /* LineAttachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineAttachment.swift; sourceTree = "<group>"; };
524528
FF7C89AF1E3BC52F000472A8 /* NSAttributedString+FontTraits.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+FontTraits.swift"; sourceTree = "<group>"; };
525529
FF7EAEC3234D253B007A26E0 /* FontProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontProvider.swift; sourceTree = "<group>"; };
530+
FF94935D245738AC0085ABB3 /* SuperscriptStringAttributeConverter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SuperscriptStringAttributeConverter.swift; sourceTree = "<group>"; };
531+
FF94935F245740250085ABB3 /* SuperscriptFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuperscriptFormatter.swift; sourceTree = "<group>"; };
532+
FF949361245744090085ABB3 /* SubscriptStringAttributeConverter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptStringAttributeConverter.swift; sourceTree = "<group>"; };
533+
FF949363245744560085ABB3 /* SubscriptFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptFormatter.swift; sourceTree = "<group>"; };
526534
FFA61E881DF18F3D00B71BF6 /* ParagraphStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParagraphStyle.swift; sourceTree = "<group>"; };
527535
FFA61EC11DF6C1C900B71BF6 /* NSAttributedString+Archive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+Archive.swift"; sourceTree = "<group>"; };
528536
FFB5D29620BEB21A0038DCFB /* CiteFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CiteFormatter.swift; sourceTree = "<group>"; };
@@ -1004,6 +1012,8 @@
10041012
F12F585F1EF20394008AE298 /* StrikethroughFormatter.swift */,
10051013
F12F58601EF20394008AE298 /* TextListFormatter.swift */,
10061014
F12F58611EF20394008AE298 /* UnderlineFormatter.swift */,
1015+
FF94935F245740250085ABB3 /* SuperscriptFormatter.swift */,
1016+
FF949363245744560085ABB3 /* SubscriptFormatter.swift */,
10071017
FF61909D202481F4004BCD0A /* CodeFormatter.swift */,
10081018
FFB5D29620BEB21A0038DCFB /* CiteFormatter.swift */,
10091019
);
@@ -1089,6 +1099,8 @@
10891099
F15BA6082151501300424120 /* BoldStringAttributeConverter.swift */,
10901100
F1656FDD2152A6A6009C7E3A /* CiteStringAttributeConverter.swift */,
10911101
F15BA60C215159A600424120 /* ItalicStringAttributeConverter.swift */,
1102+
FF94935D245738AC0085ABB3 /* SuperscriptStringAttributeConverter.swift */,
1103+
FF949361245744090085ABB3 /* SubscriptStringAttributeConverter.swift */,
10921104
F15BA60E21515C0F00424120 /* UnderlineStringAttributeConverter.swift */,
10931105
);
10941106
path = Implementations;
@@ -1587,6 +1599,7 @@
15871599
F16A2AD520CC437E00BF3A0A /* LineAttachmentToElementConverter.swift in Sources */,
15881600
B574F4A41FB0CF3B0048F355 /* NSAttributedStringKey+Conversion.swift in Sources */,
15891601
F12F58631EF20394008AE298 /* AttributeFormatter.swift in Sources */,
1602+
FF949360245740250085ABB3 /* SuperscriptFormatter.swift in Sources */,
15901603
F19544051F588F1A00671B73 /* CSSParser.swift in Sources */,
15911604
599F254E1D8BC9A1002871D6 /* FormatBarDelegate.swift in Sources */,
15921605
F109873F214FF4C400983B6A /* ElementAttributeConverter.swift in Sources */,
@@ -1630,6 +1643,7 @@
16301643
F11326B21EF1AA91007FEE9A /* HTMLPre.swift in Sources */,
16311644
F1098741214FF55F00983B6A /* BoldElementAttributeConverter.swift in Sources */,
16321645
F17BC8AB1F4E512800398E2B /* AttributedStringSerializer.swift in Sources */,
1646+
FF949364245744560085ABB3 /* SubscriptFormatter.swift in Sources */,
16331647
F18986E11EF2040A0060EDBA /* FontFormatter.swift in Sources */,
16341648
F1FF7D9D201A147B007B0B32 /* Figure.swift in Sources */,
16351649
FFA61E891DF18F3D00B71BF6 /* ParagraphStyle.swift in Sources */,
@@ -1638,6 +1652,7 @@
16381652
FF7C89B01E3BC52F000472A8 /* NSAttributedString+FontTraits.swift in Sources */,
16391653
B5E94D101FE01335000E7C20 /* FigureElementConverter.swift in Sources */,
16401654
40A2986D1FD61B0C00AEDF3B /* ElementConverter.swift in Sources */,
1655+
FF949362245744090085ABB3 /* SubscriptStringAttributeConverter.swift in Sources */,
16411656
F1FF7DA1201A1D3E007B0B32 /* FigcaptionElementConverter.swift in Sources */,
16421657
F9982CF621877663001E606B /* TextViewPasteboardDelegate.swift in Sources */,
16431658
F1289FB72155244A001E07C5 /* AttributeType.swift in Sources */,
@@ -1684,6 +1699,7 @@
16841699
F12F586F1EF20394008AE298 /* PreFormatter.swift in Sources */,
16851700
F18A1EA921C0586E00F1AA9E /* NSAttributedString+ParagraphRange.swift in Sources */,
16861701
B5B86D371DA3EC250083DB3F /* NSRange+Helpers.swift in Sources */,
1702+
FF94935E245738AC0085ABB3 /* SuperscriptStringAttributeConverter.swift in Sources */,
16871703
F15BA6172151693F00424120 /* BoldCSSAttributeMatcher.swift in Sources */,
16881704
F1E2323420C18055008DA49F /* FormatterElementConverter.swift in Sources */,
16891705
F15BA60B2151519C00424120 /* CSSAttributeType.swift in Sources */,

Aztec/Classes/Converters/ElementsToAttributedString/Implementations/GenericElementConverter.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class GenericElementConverter: ElementConverter {
1111
/// At some point we should modify how the conversion works, so that any supported element never goes through this
1212
/// converter at all, and this converter is turned into an `UnsupportedElementConverter()` exclusively.
1313
///
14-
private static let supportedElements: [Element] = [.a, .aztecRootNode, .b, .br, .blockquote, .del, .div, .em, .figure, .figcaption, .h1, .h2, .h3, .h4, .h5, .h6, .hr, .i, .img, .li, .ol, .p, .pre, .s, .span, .strike, .strong, .u, .ul, .video, .code]
14+
private static let supportedElements: [Element] = [.a, .aztecRootNode, .b, .br, .blockquote, .del, .div, .em, .figure, .figcaption, .h1, .h2, .h3, .h4, .h5, .h6, .hr, .i, .img, .li, .ol, .p, .pre, .s, .span, .strike, .strong, .u, .ul, .video, .code, .sup, .sub]
1515

1616
// MARK: - Built-in formatter instances
1717

@@ -34,6 +34,8 @@ class GenericElementConverter: ElementConverter {
3434
lazy var unorderedListFormatter = TextListFormatter(style: .unordered, increaseDepth: true)
3535
lazy var codeFormatter = CodeFormatter()
3636
lazy var liFormatter = LiFormatter()
37+
lazy var superscriptFormatter = SuperscriptFormatter()
38+
lazy var subscriptFormatter = SubscriptFormatter()
3739

3840
public lazy var elementFormattersMap: [Element: AttributeFormatter] = {
3941
return [
@@ -55,7 +57,9 @@ class GenericElementConverter: ElementConverter {
5557
.p: self.paragraphFormatter,
5658
.pre: self.preFormatter,
5759
.code: self.codeFormatter,
58-
.li: self.liFormatter
60+
.li: self.liFormatter,
61+
.sup: self.superscriptFormatter,
62+
.sub: self.subscriptFormatter,
5963
]
6064
}()
6165

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import Foundation
2+
import UIKit
3+
4+
5+
/// Converts the subscript style information from string attributes and aggregates it into an
6+
/// existing array of element nodes.
7+
///
8+
open class SubscriptStringAttributeConverter: StringAttributeConverter {
9+
10+
private let toggler = HTMLStyleToggler(defaultElement: .sub, cssAttributeMatcher: NeverCSSAttributeMatcher())
11+
12+
public func convert(
13+
attributes: [NSAttributedString.Key: Any],
14+
andAggregateWith elementNodes: [ElementNode]) -> [ElementNode] {
15+
16+
var elementNodes = elementNodes
17+
18+
// We add the representation right away, if it exists... as it could contain attributes beyond just this
19+
// style. The enable and disable methods below can modify this as necessary.
20+
//
21+
if let representation = attributes[NSAttributedString.Key.subHtmlRepresentation] as? HTMLRepresentation,
22+
case let .element(representationElement) = representation.kind {
23+
24+
elementNodes.append(representationElement.toElementNode())
25+
}
26+
27+
if shouldEnable(for: attributes) {
28+
return toggler.enable(in: elementNodes)
29+
} else {
30+
return toggler.disable(in: elementNodes)
31+
}
32+
}
33+
34+
// MARK: - Style Detection
35+
36+
func shouldEnable(for attributes: [NSAttributedString.Key : Any]) -> Bool {
37+
return hasTraits(for: attributes)
38+
}
39+
40+
func hasTraits(for attributes: [NSAttributedString.Key : Any]) -> Bool {
41+
guard let baselineOffset = attributes[.baselineOffset] as? NSNumber else {
42+
return false
43+
}
44+
45+
return baselineOffset.intValue < 0;
46+
}
47+
}
48+
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import Foundation
2+
import UIKit
3+
4+
5+
/// Converts the superscript style information from string attributes and aggregates it into an
6+
/// existing array of element nodes.
7+
///
8+
open class SuperscriptStringAttributeConverter: StringAttributeConverter {
9+
10+
private let toggler = HTMLStyleToggler(defaultElement: .sup, cssAttributeMatcher: NeverCSSAttributeMatcher())
11+
12+
public func convert(
13+
attributes: [NSAttributedString.Key: Any],
14+
andAggregateWith elementNodes: [ElementNode]) -> [ElementNode] {
15+
16+
var elementNodes = elementNodes
17+
18+
// We add the representation right away, if it exists... as it could contain attributes beyond just this
19+
// style. The enable and disable methods below can modify this as necessary.
20+
//
21+
if let representation = attributes[NSAttributedString.Key.supHtmlRepresentation] as? HTMLRepresentation,
22+
case let .element(representationElement) = representation.kind {
23+
24+
elementNodes.append(representationElement.toElementNode())
25+
}
26+
27+
if shouldEnable(for: attributes) {
28+
return toggler.enable(in: elementNodes)
29+
} else {
30+
return toggler.disable(in: elementNodes)
31+
}
32+
}
33+
34+
// MARK: - Style Detection
35+
36+
func shouldEnable(for attributes: [NSAttributedString.Key : Any]) -> Bool {
37+
return hasTraits(for: attributes)
38+
}
39+
40+
func hasTraits(for attributes: [NSAttributedString.Key : Any]) -> Bool {
41+
guard let baselineOffset = attributes[.baselineOffset] as? NSNumber else {
42+
return false
43+
}
44+
45+
return baselineOffset.intValue > 0;
46+
}
47+
}
48+

Aztec/Classes/Extensions/NSAttributedStringKey+Aztec.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,12 @@ public extension NSAttributedString.Key {
5353
/// Key used to store citeHTMLRepresentations, by our CiteFormatter.
5454
///
5555
static let citeHtmlRepresentation = NSAttributedString.Key("Cite.htmlRepresentation")
56+
57+
/// Key used to store Sup Tag Metadata, by our SupFormatter.
58+
///
59+
static let supHtmlRepresentation = NSAttributedString.Key("Sup.htmlRepresentation")
60+
61+
/// Key used to store Sub Tag Metadata, by our SupFormatter.
62+
///
63+
static let subHtmlRepresentation = NSAttributedString.Key("Sub.htmlRepresentation")
5664
}

Aztec/Classes/Extensions/UITextView+Delegate.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,11 @@ extension UITextView {
1212
delegate?.textViewDidChange?(self)
1313
NotificationCenter.default.post(name: UITextView.textDidChangeNotification, object: self)
1414
}
15+
16+
final func shouldChangeText(in range: NSRange, with text:String) -> Bool {
17+
guard let result = self.delegate?.textView?(self, shouldChangeTextIn: range, replacementText: text) else {
18+
return true
19+
}
20+
return result
21+
}
1522
}

Aztec/Classes/Formatters/Base/StandardAttributeFormatter.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@ class StandardAttributeFormatter: AttributeFormatter {
1111

1212
let htmlRepresentationKey: NSAttributedString.Key
1313

14+
let needsToMatchValue: Bool
15+
1416
// MARK: - Init
1517

16-
init(attributeKey: NSAttributedString.Key, attributeValue: Any, htmlRepresentationKey: NSAttributedString.Key) {
18+
init(attributeKey: NSAttributedString.Key, attributeValue: Any, htmlRepresentationKey: NSAttributedString.Key, needsToMatchValue: Bool = false) {
1719
self.attributeKey = attributeKey
1820
self.attributeValue = attributeValue
1921
self.htmlRepresentationKey = htmlRepresentationKey
22+
self.needsToMatchValue = needsToMatchValue
2023
}
2124

2225
func applicationRange(for range: NSRange, in text: NSAttributedString) -> NSRange {
@@ -43,7 +46,16 @@ class StandardAttributeFormatter: AttributeFormatter {
4346

4447
func present(in attributes: [NSAttributedString.Key: Any]) -> Bool {
4548
let enabled = attributes[attributeKey] != nil
46-
return enabled
49+
if (!needsToMatchValue) {
50+
return enabled
51+
}
52+
53+
if let value = attributes[attributeKey] as? NSObject,
54+
let attributeValue = attributeValue as? NSObject {
55+
return value.isEqual(attributeValue)
56+
}
57+
58+
return false
4759
}
4860
}
4961

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import UIKit
2+
3+
class SubscriptFormatter: StandardAttributeFormatter {
4+
5+
init() {
6+
super.init(attributeKey: .baselineOffset,
7+
attributeValue: NSNumber(-4),
8+
htmlRepresentationKey: .subHtmlRepresentation)
9+
}
10+
11+
override func apply(to attributes: [NSAttributedString.Key: Any], andStore representation: HTMLRepresentation?) -> [NSAttributedString.Key: Any] {
12+
var resultingAttributes = super.apply(to: attributes, andStore: representation)
13+
guard let currentFont = attributes[.font] as? UIFont else {
14+
return resultingAttributes
15+
}
16+
let font = UIFont(descriptor: currentFont.fontDescriptor, size: currentFont.pointSize - 2)
17+
resultingAttributes[.font] = font
18+
return resultingAttributes
19+
}
20+
21+
override func remove(from attributes: [NSAttributedString.Key: Any]) -> [NSAttributedString.Key: Any] {
22+
var resultingAttributes = super.remove(from: attributes)
23+
24+
guard let currentFont = attributes[.font] as? UIFont else {
25+
return resultingAttributes
26+
}
27+
let font = UIFont(descriptor: currentFont.fontDescriptor, size: currentFont.pointSize + 2)
28+
resultingAttributes[.font] = font
29+
30+
return resultingAttributes
31+
}
32+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import UIKit
2+
3+
class SuperscriptFormatter: StandardAttributeFormatter {
4+
5+
init() {
6+
super.init(attributeKey: .baselineOffset,
7+
attributeValue: NSNumber(4),
8+
htmlRepresentationKey: .supHtmlRepresentation)
9+
}
10+
11+
override func apply(to attributes: [NSAttributedString.Key: Any], andStore representation: HTMLRepresentation?) -> [NSAttributedString.Key: Any] {
12+
var resultingAttributes = super.apply(to: attributes, andStore: representation)
13+
guard let currentFont = attributes[.font] as? UIFont else {
14+
return resultingAttributes
15+
}
16+
let font = UIFont(descriptor: currentFont.fontDescriptor, size: currentFont.pointSize - 2)
17+
resultingAttributes[.font] = font
18+
return resultingAttributes
19+
}
20+
21+
override func remove(from attributes: [NSAttributedString.Key: Any]) -> [NSAttributedString.Key: Any] {
22+
var resultingAttributes = super.remove(from: attributes)
23+
24+
guard let currentFont = attributes[.font] as? UIFont else {
25+
return resultingAttributes
26+
}
27+
let font = UIFont(descriptor: currentFont.fontDescriptor, size: currentFont.pointSize + 2)
28+
resultingAttributes[.font] = font
29+
30+
return resultingAttributes
31+
}
32+
}

Aztec/Classes/Libxml2/DOM/Data/Element.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public struct Element: RawRepresentable, Hashable {
3030
public static var mergeableBlockLevelElements = Set<Element>([.blockquote, .div, .figure, .figcaption, .h1, .h2, .h3, .h4, .h5, .h6, .hr, .li, .ol, .ul, .p, .pre])
3131

3232
/// List of style HTML elements that can be merged together when they are sibling to each other
33-
public static var mergeableStyleElements = Set<Element>([.i, .em, .b, .strong, .strike, .u, .code, .cite, .a])
33+
public static var mergeableStyleElements = Set<Element>([.i, .em, .b, .strong, .strike, .u, .code, .cite, .a, .sup, .sub])
3434

3535
/// List of block level elements that can be merged but only when they have a single children that is also mergeable
3636
///
@@ -106,6 +106,8 @@ extension Element {
106106
public static let span = Element("span")
107107
public static let strike = Element("strike")
108108
public static let strong = Element("strong")
109+
public static let sub = Element("sub")
110+
public static let sup = Element("sup")
109111
public static let table = Element("table")
110112
public static let tbody = Element("tbody")
111113
public static let td = Element("td")

0 commit comments

Comments
 (0)