From 717966485763549b8ede4b1f318997c4ac7d8851 Mon Sep 17 00:00:00 2001 From: Sergio Estevao Date: Mon, 27 Apr 2020 12:43:20 +0100 Subject: [PATCH 01/11] Check with the delegate if changes are allowed when custom pastes happen. --- .../Extensions/UITextView+Delegate.swift | 7 +++++++ .../TextKit/TextViewPasteboardDelegate.swift | 18 ++++++++++++++++++ Example/Example/EditorDemoController.swift | 4 ++++ .../Embeds/WordPressPasteboardDelegate.swift | 9 +++++---- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Aztec/Classes/Extensions/UITextView+Delegate.swift b/Aztec/Classes/Extensions/UITextView+Delegate.swift index 9ba6689c0..cd00335c0 100644 --- a/Aztec/Classes/Extensions/UITextView+Delegate.swift +++ b/Aztec/Classes/Extensions/UITextView+Delegate.swift @@ -12,4 +12,11 @@ extension UITextView { delegate?.textViewDidChange?(self) NotificationCenter.default.post(name: UITextView.textDidChangeNotification, object: self) } + + final func shouldChangeText(in range: NSRange, with text:String) -> Bool { + guard let result = self.delegate?.textView?(self, shouldChangeTextIn: range, replacementText: text) else { + return true + } + return result + } } diff --git a/Aztec/Classes/TextKit/TextViewPasteboardDelegate.swift b/Aztec/Classes/TextKit/TextViewPasteboardDelegate.swift index fd177471a..92a0c13ec 100644 --- a/Aztec/Classes/TextKit/TextViewPasteboardDelegate.swift +++ b/Aztec/Classes/TextKit/TextViewPasteboardDelegate.swift @@ -28,6 +28,9 @@ open class AztecTextViewPasteboardDelegate: TextViewPasteboardDelegate { let selectedRange = textView.selectedRange if selectedRange.length == 0 { + guard textView.shouldChangeText(in: selectedRange, with: url.absoluteString) else { + return true + } textView.setLink(url, title:url.absoluteString, inRange: selectedRange) } else { textView.setLink(url, inRange: selectedRange) @@ -45,6 +48,10 @@ open class AztecTextViewPasteboardDelegate: TextViewPasteboardDelegate { textView.storage.htmlConverter.isSupported(html) else { return false } + let string = textView.storage.htmlConverter.attributedString(from: html) + guard textView.shouldChangeText(in: textView.selectedRange, with: string.string) else { + return true + } textView.replace(textView.selectedRange, withHTML: html) return true @@ -60,6 +67,11 @@ open class AztecTextViewPasteboardDelegate: TextViewPasteboardDelegate { } string.loadLazyAttachments() let selectedRange = textView.selectedRange + + guard textView.shouldChangeText(in: selectedRange, with: string.string) else { + return true + } + let storage = textView.storage let finalRange = NSRange(location: selectedRange.location, length: string.length) @@ -105,6 +117,11 @@ open class AztecTextViewPasteboardDelegate: TextViewPasteboardDelegate { } let selectedRange = textView.selectedRange + + guard textView.shouldChangeText(in: selectedRange, with: string.string) else { + return true + } + let finalRange = NSRange(location: selectedRange.location, length: string.length) let originalText = textView.attributedText.attributedSubstring(from: selectedRange) @@ -133,4 +150,5 @@ open class AztecTextViewPasteboardDelegate: TextViewPasteboardDelegate { return true } + } diff --git a/Example/Example/EditorDemoController.swift b/Example/Example/EditorDemoController.swift index 186a21c9b..792e6771e 100644 --- a/Example/Example/EditorDemoController.swift +++ b/Example/Example/EditorDemoController.swift @@ -534,6 +534,10 @@ extension EditorDemoController : UITextViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { updateTitlePosition() } + + func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { + return true + } } extension EditorDemoController : Aztec.TextViewFormattingDelegate { diff --git a/WordPressEditor/WordPressEditor/Classes/Plugins/WordPressPlugin/Calypso/Embeds/WordPressPasteboardDelegate.swift b/WordPressEditor/WordPressEditor/Classes/Plugins/WordPressPlugin/Calypso/Embeds/WordPressPasteboardDelegate.swift index 3e2092747..db138de82 100644 --- a/WordPressEditor/WordPressEditor/Classes/Plugins/WordPressPlugin/Calypso/Embeds/WordPressPasteboardDelegate.swift +++ b/WordPressEditor/WordPressEditor/Classes/Plugins/WordPressPlugin/Calypso/Embeds/WordPressPasteboardDelegate.swift @@ -17,10 +17,11 @@ class WordPressTextViewPasteboardDelegate: AztecTextViewPasteboardDelegate { } let result = super.tryPastingString(in: textView) - - // Bump the input to the next line – we need the embed link to be the only - // text on this line – otherwise it can't be autoconverted. - textView.insertText(String(.lineSeparator)) + if result { + // Bump the input to the next line – we need the embed link to be the only + // text on this line – otherwise it can't be autoconverted. + textView.insertText(String(.lineSeparator)) + } return result } From 962e1e52f448831e4a771b8b24061f5995c43448 Mon Sep 17 00:00:00 2001 From: Sergio Estevao Date: Mon, 27 Apr 2020 17:45:29 +0100 Subject: [PATCH 02/11] Add generic support for sup sub html elements. --- .../Implementations/GenericElementConverter.swift | 2 +- .../Classes/Extensions/NSAttributedStringKey+Aztec.swift | 8 ++++++++ Aztec/Classes/Libxml2/DOM/Data/Element.swift | 4 +++- Example/Example/SampleContent/content.html | 3 ++- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Aztec/Classes/Converters/ElementsToAttributedString/Implementations/GenericElementConverter.swift b/Aztec/Classes/Converters/ElementsToAttributedString/Implementations/GenericElementConverter.swift index 6f1451be9..54b234dce 100644 --- a/Aztec/Classes/Converters/ElementsToAttributedString/Implementations/GenericElementConverter.swift +++ b/Aztec/Classes/Converters/ElementsToAttributedString/Implementations/GenericElementConverter.swift @@ -11,7 +11,7 @@ class GenericElementConverter: ElementConverter { /// At some point we should modify how the conversion works, so that any supported element never goes through this /// converter at all, and this converter is turned into an `UnsupportedElementConverter()` exclusively. /// - 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] + 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] // MARK: - Built-in formatter instances diff --git a/Aztec/Classes/Extensions/NSAttributedStringKey+Aztec.swift b/Aztec/Classes/Extensions/NSAttributedStringKey+Aztec.swift index 3b5a77b49..d426bc5e2 100644 --- a/Aztec/Classes/Extensions/NSAttributedStringKey+Aztec.swift +++ b/Aztec/Classes/Extensions/NSAttributedStringKey+Aztec.swift @@ -53,4 +53,12 @@ public extension NSAttributedString.Key { /// Key used to store citeHTMLRepresentations, by our CiteFormatter. /// static let citeHtmlRepresentation = NSAttributedString.Key("Cite.htmlRepresentation") + + /// Key used to store Sup Tag Metadata, by our SupFormatter. + /// + static let supHtmlRepresentation = NSAttributedString.Key("Sup.htmlRepresentation") + + /// Key used to store Sub Tag Metadata, by our SupFormatter. + /// + static let subHtmlRepresentation = NSAttributedString.Key("Sub.htmlRepresentation") } diff --git a/Aztec/Classes/Libxml2/DOM/Data/Element.swift b/Aztec/Classes/Libxml2/DOM/Data/Element.swift index 2f208af83..308f73d35 100644 --- a/Aztec/Classes/Libxml2/DOM/Data/Element.swift +++ b/Aztec/Classes/Libxml2/DOM/Data/Element.swift @@ -30,7 +30,7 @@ public struct Element: RawRepresentable, Hashable { public static var mergeableBlockLevelElements = Set([.blockquote, .div, .figure, .figcaption, .h1, .h2, .h3, .h4, .h5, .h6, .hr, .li, .ol, .ul, .p, .pre]) /// List of style HTML elements that can be merged together when they are sibling to each other - public static var mergeableStyleElements = Set([.i, .em, .b, .strong, .strike, .u, .code, .cite, .a]) + public static var mergeableStyleElements = Set([.i, .em, .b, .strong, .strike, .u, .code, .cite, .a, .sup, .sub]) /// List of block level elements that can be merged but only when they have a single children that is also mergeable /// @@ -106,6 +106,8 @@ extension Element { public static let span = Element("span") public static let strike = Element("strike") public static let strong = Element("strong") + public static let sub = Element("sub") + public static let sup = Element("sup") public static let table = Element("table") public static let tbody = Element("tbody") public static let td = Element("td") diff --git a/Example/Example/SampleContent/content.html b/Example/Example/SampleContent/content.html index 2da6a99ed..510adf670 100644 --- a/Example/Example/SampleContent/content.html +++ b/Example/Example/SampleContent/content.html @@ -15,7 +15,8 @@

Character Styles

Colors: Royal Blue by name Red by hex

Underline with CSS: Alternative underline text
Links: I'm a link!
Open in new window link!
-Code: print("Hello world") +Code: print("Hello world")
+This is superscript(1) and this is subscript(2)
From 04df855fc1999c82f2f5c97bfeff9e767a2241fe Mon Sep 17 00:00:00 2001 From: Sergio Estevao Date: Mon, 27 Apr 2020 17:45:57 +0100 Subject: [PATCH 03/11] Add an empty CSS Attribute matcher. --- .../Classes/Libxml2/DOM/Logic/CSS/CSSAttributeMatcher.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Aztec/Classes/Libxml2/DOM/Logic/CSS/CSSAttributeMatcher.swift b/Aztec/Classes/Libxml2/DOM/Logic/CSS/CSSAttributeMatcher.swift index 9e84471eb..0b0371475 100644 --- a/Aztec/Classes/Libxml2/DOM/Logic/CSS/CSSAttributeMatcher.swift +++ b/Aztec/Classes/Libxml2/DOM/Logic/CSS/CSSAttributeMatcher.swift @@ -3,3 +3,9 @@ import Foundation public protocol CSSAttributeMatcher { func check(_ cssAttribute: CSSAttribute) -> Bool } + +open class NeverCSSAttributeMatcher: CSSAttributeMatcher { + public func check(_ cssAttribute: CSSAttribute) -> Bool { + return false + } +} From 4e99e83ddbdb0eda214cec3490cae66c18700ff8 Mon Sep 17 00:00:00 2001 From: Sergio Estevao Date: Mon, 27 Apr 2020 17:46:25 +0100 Subject: [PATCH 04/11] Add superscript formatter and converters. --- Aztec.xcodeproj/project.pbxproj | 8 ++++ .../GenericElementConverter.swift | 4 +- .../SuperscriptStringAttributeConverter.swift | 48 +++++++++++++++++++ .../Base/StandardAttributeFormatter.swift | 16 ++++++- .../SuperscriptFormatter.swift | 10 ++++ .../Conversions/AttributedStringParser.swift | 1 + 6 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SuperscriptStringAttributeConverter.swift create mode 100644 Aztec/Classes/Formatters/Implementations/SuperscriptFormatter.swift diff --git a/Aztec.xcodeproj/project.pbxproj b/Aztec.xcodeproj/project.pbxproj index 76ee02409..ae5e6e644 100644 --- a/Aztec.xcodeproj/project.pbxproj +++ b/Aztec.xcodeproj/project.pbxproj @@ -239,6 +239,8 @@ FF7A1C511E5651EA00C4C7C8 /* LineAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7A1C501E5651EA00C4C7C8 /* LineAttachment.swift */; }; FF7C89B01E3BC52F000472A8 /* NSAttributedString+FontTraits.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7C89AF1E3BC52F000472A8 /* NSAttributedString+FontTraits.swift */; }; FF7EAEC4234D253B007A26E0 /* FontProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7EAEC3234D253B007A26E0 /* FontProvider.swift */; }; + FF94935E245738AC0085ABB3 /* SuperscriptStringAttributeConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF94935D245738AC0085ABB3 /* SuperscriptStringAttributeConverter.swift */; }; + FF949360245740250085ABB3 /* SuperscriptFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF94935F245740250085ABB3 /* SuperscriptFormatter.swift */; }; FFA61E891DF18F3D00B71BF6 /* ParagraphStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA61E881DF18F3D00B71BF6 /* ParagraphStyle.swift */; }; FFA61EC21DF6C1C900B71BF6 /* NSAttributedString+Archive.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA61EC11DF6C1C900B71BF6 /* NSAttributedString+Archive.swift */; }; FFB5D29720BEB21A0038DCFB /* CiteFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB5D29620BEB21A0038DCFB /* CiteFormatter.swift */; }; @@ -523,6 +525,8 @@ FF7A1C501E5651EA00C4C7C8 /* LineAttachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineAttachment.swift; sourceTree = ""; }; FF7C89AF1E3BC52F000472A8 /* NSAttributedString+FontTraits.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+FontTraits.swift"; sourceTree = ""; }; FF7EAEC3234D253B007A26E0 /* FontProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontProvider.swift; sourceTree = ""; }; + FF94935D245738AC0085ABB3 /* SuperscriptStringAttributeConverter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SuperscriptStringAttributeConverter.swift; sourceTree = ""; }; + FF94935F245740250085ABB3 /* SuperscriptFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuperscriptFormatter.swift; sourceTree = ""; }; FFA61E881DF18F3D00B71BF6 /* ParagraphStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParagraphStyle.swift; sourceTree = ""; }; FFA61EC11DF6C1C900B71BF6 /* NSAttributedString+Archive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+Archive.swift"; sourceTree = ""; }; FFB5D29620BEB21A0038DCFB /* CiteFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CiteFormatter.swift; sourceTree = ""; }; @@ -1004,6 +1008,7 @@ F12F585F1EF20394008AE298 /* StrikethroughFormatter.swift */, F12F58601EF20394008AE298 /* TextListFormatter.swift */, F12F58611EF20394008AE298 /* UnderlineFormatter.swift */, + FF94935F245740250085ABB3 /* SuperscriptFormatter.swift */, FF61909D202481F4004BCD0A /* CodeFormatter.swift */, FFB5D29620BEB21A0038DCFB /* CiteFormatter.swift */, ); @@ -1089,6 +1094,7 @@ F15BA6082151501300424120 /* BoldStringAttributeConverter.swift */, F1656FDD2152A6A6009C7E3A /* CiteStringAttributeConverter.swift */, F15BA60C215159A600424120 /* ItalicStringAttributeConverter.swift */, + FF94935D245738AC0085ABB3 /* SuperscriptStringAttributeConverter.swift */, F15BA60E21515C0F00424120 /* UnderlineStringAttributeConverter.swift */, ); path = Implementations; @@ -1587,6 +1593,7 @@ F16A2AD520CC437E00BF3A0A /* LineAttachmentToElementConverter.swift in Sources */, B574F4A41FB0CF3B0048F355 /* NSAttributedStringKey+Conversion.swift in Sources */, F12F58631EF20394008AE298 /* AttributeFormatter.swift in Sources */, + FF949360245740250085ABB3 /* SuperscriptFormatter.swift in Sources */, F19544051F588F1A00671B73 /* CSSParser.swift in Sources */, 599F254E1D8BC9A1002871D6 /* FormatBarDelegate.swift in Sources */, F109873F214FF4C400983B6A /* ElementAttributeConverter.swift in Sources */, @@ -1684,6 +1691,7 @@ F12F586F1EF20394008AE298 /* PreFormatter.swift in Sources */, F18A1EA921C0586E00F1AA9E /* NSAttributedString+ParagraphRange.swift in Sources */, B5B86D371DA3EC250083DB3F /* NSRange+Helpers.swift in Sources */, + FF94935E245738AC0085ABB3 /* SuperscriptStringAttributeConverter.swift in Sources */, F15BA6172151693F00424120 /* BoldCSSAttributeMatcher.swift in Sources */, F1E2323420C18055008DA49F /* FormatterElementConverter.swift in Sources */, F15BA60B2151519C00424120 /* CSSAttributeType.swift in Sources */, diff --git a/Aztec/Classes/Converters/ElementsToAttributedString/Implementations/GenericElementConverter.swift b/Aztec/Classes/Converters/ElementsToAttributedString/Implementations/GenericElementConverter.swift index 54b234dce..9a9ca0398 100644 --- a/Aztec/Classes/Converters/ElementsToAttributedString/Implementations/GenericElementConverter.swift +++ b/Aztec/Classes/Converters/ElementsToAttributedString/Implementations/GenericElementConverter.swift @@ -34,6 +34,7 @@ class GenericElementConverter: ElementConverter { lazy var unorderedListFormatter = TextListFormatter(style: .unordered, increaseDepth: true) lazy var codeFormatter = CodeFormatter() lazy var liFormatter = LiFormatter() + lazy var superscriptFormatter = SuperscriptFormatter() public lazy var elementFormattersMap: [Element: AttributeFormatter] = { return [ @@ -55,7 +56,8 @@ class GenericElementConverter: ElementConverter { .p: self.paragraphFormatter, .pre: self.preFormatter, .code: self.codeFormatter, - .li: self.liFormatter + .li: self.liFormatter, + .sup: self.superscriptFormatter, ] }() diff --git a/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SuperscriptStringAttributeConverter.swift b/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SuperscriptStringAttributeConverter.swift new file mode 100644 index 000000000..b564e27bc --- /dev/null +++ b/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SuperscriptStringAttributeConverter.swift @@ -0,0 +1,48 @@ +import Foundation +import UIKit + + +/// Converts the superscript style information from string attributes and aggregates it into an +/// existing array of element nodes. +/// +open class SuperscriptStringAttributeConverter: StringAttributeConverter { + + private let toggler = HTMLStyleToggler(defaultElement: .sup, cssAttributeMatcher: NeverCSSAttributeMatcher()) + + public func convert( + attributes: [NSAttributedString.Key: Any], + andAggregateWith elementNodes: [ElementNode]) -> [ElementNode] { + + var elementNodes = elementNodes + + // We add the representation right away, if it exists... as it could contain attributes beyond just this + // style. The enable and disable methods below can modify this as necessary. + // + if let representation = attributes[NSAttributedString.Key.supHtmlRepresentation] as? HTMLRepresentation, + case let .element(representationElement) = representation.kind { + + elementNodes.append(representationElement.toElementNode()) + } + + if shouldEnable(for: attributes) { + return toggler.enable(in: elementNodes) + } else { + return toggler.disable(in: elementNodes) + } + } + + // MARK: - Style Detection + + func shouldEnable(for attributes: [NSAttributedString.Key : Any]) -> Bool { + return hasTraits(for: attributes) + } + + func hasTraits(for attributes: [NSAttributedString.Key : Any]) -> Bool { + guard let baselineOffset = attributes[.baselineOffset] as? NSNumber else { + return false + } + + return baselineOffset.intValue > 0; + } +} + diff --git a/Aztec/Classes/Formatters/Base/StandardAttributeFormatter.swift b/Aztec/Classes/Formatters/Base/StandardAttributeFormatter.swift index 7f85c1bb1..6f3626c51 100644 --- a/Aztec/Classes/Formatters/Base/StandardAttributeFormatter.swift +++ b/Aztec/Classes/Formatters/Base/StandardAttributeFormatter.swift @@ -11,12 +11,15 @@ class StandardAttributeFormatter: AttributeFormatter { let htmlRepresentationKey: NSAttributedString.Key + let needsToMatchValue: Bool + // MARK: - Init - init(attributeKey: NSAttributedString.Key, attributeValue: Any, htmlRepresentationKey: NSAttributedString.Key) { + init(attributeKey: NSAttributedString.Key, attributeValue: Any, htmlRepresentationKey: NSAttributedString.Key, needsToMatchValue: Bool = false) { self.attributeKey = attributeKey self.attributeValue = attributeValue self.htmlRepresentationKey = htmlRepresentationKey + self.needsToMatchValue = needsToMatchValue } func applicationRange(for range: NSRange, in text: NSAttributedString) -> NSRange { @@ -43,7 +46,16 @@ class StandardAttributeFormatter: AttributeFormatter { func present(in attributes: [NSAttributedString.Key: Any]) -> Bool { let enabled = attributes[attributeKey] != nil - return enabled + if (!needsToMatchValue) { + return enabled + } + + if let value = attributes[attributeKey] as? NSObject, + let attributeValue = attributeValue as? NSObject { + return value.isEqual(attributeValue) + } + + return false } } diff --git a/Aztec/Classes/Formatters/Implementations/SuperscriptFormatter.swift b/Aztec/Classes/Formatters/Implementations/SuperscriptFormatter.swift new file mode 100644 index 000000000..64424c025 --- /dev/null +++ b/Aztec/Classes/Formatters/Implementations/SuperscriptFormatter.swift @@ -0,0 +1,10 @@ +import UIKit + +class SuperscriptFormatter: StandardAttributeFormatter { + + init() { + super.init(attributeKey: .baselineOffset, + attributeValue: NSNumber(4), + htmlRepresentationKey: .supHtmlRepresentation) + } +} diff --git a/Aztec/Classes/NSAttributedString/Conversions/AttributedStringParser.swift b/Aztec/Classes/NSAttributedString/Conversions/AttributedStringParser.swift index 84f763f4a..43c2137a7 100644 --- a/Aztec/Classes/NSAttributedString/Conversions/AttributedStringParser.swift +++ b/Aztec/Classes/NSAttributedString/Conversions/AttributedStringParser.swift @@ -27,6 +27,7 @@ class AttributedStringParser { BoldStringAttributeConverter(), ConditionalItalicStringAttributeConverter(), UnderlineStringAttributeConverter(), + SuperscriptStringAttributeConverter(), ] // MARK: - Attachment Converters From f2425b83f622cd3a676d55f067b77e36909ec452 Mon Sep 17 00:00:00 2001 From: Sergio Estevao Date: Mon, 27 Apr 2020 17:51:21 +0100 Subject: [PATCH 05/11] Add support for style of subscript. --- Aztec.xcodeproj/project.pbxproj | 8 ++++ .../GenericElementConverter.swift | 2 + .../SubscriptStringAttributeConverter.swift | 48 +++++++++++++++++++ .../Implementations/SubscriptFormatter.swift | 10 ++++ .../Conversions/AttributedStringParser.swift | 1 + 5 files changed, 69 insertions(+) create mode 100644 Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SubscriptStringAttributeConverter.swift create mode 100644 Aztec/Classes/Formatters/Implementations/SubscriptFormatter.swift diff --git a/Aztec.xcodeproj/project.pbxproj b/Aztec.xcodeproj/project.pbxproj index ae5e6e644..ca22c3c9d 100644 --- a/Aztec.xcodeproj/project.pbxproj +++ b/Aztec.xcodeproj/project.pbxproj @@ -241,6 +241,8 @@ FF7EAEC4234D253B007A26E0 /* FontProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF7EAEC3234D253B007A26E0 /* FontProvider.swift */; }; FF94935E245738AC0085ABB3 /* SuperscriptStringAttributeConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF94935D245738AC0085ABB3 /* SuperscriptStringAttributeConverter.swift */; }; FF949360245740250085ABB3 /* SuperscriptFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF94935F245740250085ABB3 /* SuperscriptFormatter.swift */; }; + FF949362245744090085ABB3 /* SubscriptStringAttributeConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF949361245744090085ABB3 /* SubscriptStringAttributeConverter.swift */; }; + FF949364245744560085ABB3 /* SubscriptFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF949363245744560085ABB3 /* SubscriptFormatter.swift */; }; FFA61E891DF18F3D00B71BF6 /* ParagraphStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA61E881DF18F3D00B71BF6 /* ParagraphStyle.swift */; }; FFA61EC21DF6C1C900B71BF6 /* NSAttributedString+Archive.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA61EC11DF6C1C900B71BF6 /* NSAttributedString+Archive.swift */; }; FFB5D29720BEB21A0038DCFB /* CiteFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB5D29620BEB21A0038DCFB /* CiteFormatter.swift */; }; @@ -527,6 +529,8 @@ FF7EAEC3234D253B007A26E0 /* FontProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontProvider.swift; sourceTree = ""; }; FF94935D245738AC0085ABB3 /* SuperscriptStringAttributeConverter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SuperscriptStringAttributeConverter.swift; sourceTree = ""; }; FF94935F245740250085ABB3 /* SuperscriptFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuperscriptFormatter.swift; sourceTree = ""; }; + FF949361245744090085ABB3 /* SubscriptStringAttributeConverter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptStringAttributeConverter.swift; sourceTree = ""; }; + FF949363245744560085ABB3 /* SubscriptFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptFormatter.swift; sourceTree = ""; }; FFA61E881DF18F3D00B71BF6 /* ParagraphStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParagraphStyle.swift; sourceTree = ""; }; FFA61EC11DF6C1C900B71BF6 /* NSAttributedString+Archive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+Archive.swift"; sourceTree = ""; }; FFB5D29620BEB21A0038DCFB /* CiteFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CiteFormatter.swift; sourceTree = ""; }; @@ -1009,6 +1013,7 @@ F12F58601EF20394008AE298 /* TextListFormatter.swift */, F12F58611EF20394008AE298 /* UnderlineFormatter.swift */, FF94935F245740250085ABB3 /* SuperscriptFormatter.swift */, + FF949363245744560085ABB3 /* SubscriptFormatter.swift */, FF61909D202481F4004BCD0A /* CodeFormatter.swift */, FFB5D29620BEB21A0038DCFB /* CiteFormatter.swift */, ); @@ -1095,6 +1100,7 @@ F1656FDD2152A6A6009C7E3A /* CiteStringAttributeConverter.swift */, F15BA60C215159A600424120 /* ItalicStringAttributeConverter.swift */, FF94935D245738AC0085ABB3 /* SuperscriptStringAttributeConverter.swift */, + FF949361245744090085ABB3 /* SubscriptStringAttributeConverter.swift */, F15BA60E21515C0F00424120 /* UnderlineStringAttributeConverter.swift */, ); path = Implementations; @@ -1637,6 +1643,7 @@ F11326B21EF1AA91007FEE9A /* HTMLPre.swift in Sources */, F1098741214FF55F00983B6A /* BoldElementAttributeConverter.swift in Sources */, F17BC8AB1F4E512800398E2B /* AttributedStringSerializer.swift in Sources */, + FF949364245744560085ABB3 /* SubscriptFormatter.swift in Sources */, F18986E11EF2040A0060EDBA /* FontFormatter.swift in Sources */, F1FF7D9D201A147B007B0B32 /* Figure.swift in Sources */, FFA61E891DF18F3D00B71BF6 /* ParagraphStyle.swift in Sources */, @@ -1645,6 +1652,7 @@ FF7C89B01E3BC52F000472A8 /* NSAttributedString+FontTraits.swift in Sources */, B5E94D101FE01335000E7C20 /* FigureElementConverter.swift in Sources */, 40A2986D1FD61B0C00AEDF3B /* ElementConverter.swift in Sources */, + FF949362245744090085ABB3 /* SubscriptStringAttributeConverter.swift in Sources */, F1FF7DA1201A1D3E007B0B32 /* FigcaptionElementConverter.swift in Sources */, F9982CF621877663001E606B /* TextViewPasteboardDelegate.swift in Sources */, F1289FB72155244A001E07C5 /* AttributeType.swift in Sources */, diff --git a/Aztec/Classes/Converters/ElementsToAttributedString/Implementations/GenericElementConverter.swift b/Aztec/Classes/Converters/ElementsToAttributedString/Implementations/GenericElementConverter.swift index 9a9ca0398..80f64de3a 100644 --- a/Aztec/Classes/Converters/ElementsToAttributedString/Implementations/GenericElementConverter.swift +++ b/Aztec/Classes/Converters/ElementsToAttributedString/Implementations/GenericElementConverter.swift @@ -35,6 +35,7 @@ class GenericElementConverter: ElementConverter { lazy var codeFormatter = CodeFormatter() lazy var liFormatter = LiFormatter() lazy var superscriptFormatter = SuperscriptFormatter() + lazy var subscriptFormatter = SubscriptFormatter() public lazy var elementFormattersMap: [Element: AttributeFormatter] = { return [ @@ -58,6 +59,7 @@ class GenericElementConverter: ElementConverter { .code: self.codeFormatter, .li: self.liFormatter, .sup: self.superscriptFormatter, + .sub: self.subscriptFormatter, ] }() diff --git a/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SubscriptStringAttributeConverter.swift b/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SubscriptStringAttributeConverter.swift new file mode 100644 index 000000000..cf83f59aa --- /dev/null +++ b/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SubscriptStringAttributeConverter.swift @@ -0,0 +1,48 @@ +import Foundation +import UIKit + + +/// Converts the subscript style information from string attributes and aggregates it into an +/// existing array of element nodes. +/// +open class SubscriptStringAttributeConverter: StringAttributeConverter { + + private let toggler = HTMLStyleToggler(defaultElement: .sub, cssAttributeMatcher: NeverCSSAttributeMatcher()) + + public func convert( + attributes: [NSAttributedString.Key: Any], + andAggregateWith elementNodes: [ElementNode]) -> [ElementNode] { + + var elementNodes = elementNodes + + // We add the representation right away, if it exists... as it could contain attributes beyond just this + // style. The enable and disable methods below can modify this as necessary. + // + if let representation = attributes[NSAttributedString.Key.subHtmlRepresentation] as? HTMLRepresentation, + case let .element(representationElement) = representation.kind { + + elementNodes.append(representationElement.toElementNode()) + } + + if shouldEnable(for: attributes) { + return toggler.enable(in: elementNodes) + } else { + return toggler.disable(in: elementNodes) + } + } + + // MARK: - Style Detection + + func shouldEnable(for attributes: [NSAttributedString.Key : Any]) -> Bool { + return hasTraits(for: attributes) + } + + func hasTraits(for attributes: [NSAttributedString.Key : Any]) -> Bool { + guard let baselineOffset = attributes[.baselineOffset] as? NSNumber else { + return false + } + + return baselineOffset.intValue < 0; + } +} + diff --git a/Aztec/Classes/Formatters/Implementations/SubscriptFormatter.swift b/Aztec/Classes/Formatters/Implementations/SubscriptFormatter.swift new file mode 100644 index 000000000..6bfd51b50 --- /dev/null +++ b/Aztec/Classes/Formatters/Implementations/SubscriptFormatter.swift @@ -0,0 +1,10 @@ +import UIKit + +class SubscriptFormatter: StandardAttributeFormatter { + + init() { + super.init(attributeKey: .baselineOffset, + attributeValue: NSNumber(-4), + htmlRepresentationKey: .subHtmlRepresentation) + } +} diff --git a/Aztec/Classes/NSAttributedString/Conversions/AttributedStringParser.swift b/Aztec/Classes/NSAttributedString/Conversions/AttributedStringParser.swift index 43c2137a7..e64f39e3d 100644 --- a/Aztec/Classes/NSAttributedString/Conversions/AttributedStringParser.swift +++ b/Aztec/Classes/NSAttributedString/Conversions/AttributedStringParser.swift @@ -28,6 +28,7 @@ class AttributedStringParser { ConditionalItalicStringAttributeConverter(), UnderlineStringAttributeConverter(), SuperscriptStringAttributeConverter(), + SubscriptStringAttributeConverter(), ] // MARK: - Attachment Converters From 5d73c1e185eee7ccd8c775ad644986bfb973fb83 Mon Sep 17 00:00:00 2001 From: Sergio Estevao Date: Mon, 27 Apr 2020 22:47:45 +0100 Subject: [PATCH 06/11] Reduce font size for superscript and subscript. --- .../SubscriptStringAttributeConverter.swift | 2 +- .../SuperscriptStringAttributeConverter.swift | 2 +- .../Implementations/SubscriptFormatter.swift | 22 +++++++++++++++++++ .../SuperscriptFormatter.swift | 22 +++++++++++++++++++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SubscriptStringAttributeConverter.swift b/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SubscriptStringAttributeConverter.swift index cf83f59aa..d1914c5b9 100644 --- a/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SubscriptStringAttributeConverter.swift +++ b/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SubscriptStringAttributeConverter.swift @@ -42,7 +42,7 @@ open class SubscriptStringAttributeConverter: StringAttributeConverter { return false } - return baselineOffset.intValue < 0; + return baselineOffset.intValue <= -4; } } diff --git a/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SuperscriptStringAttributeConverter.swift b/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SuperscriptStringAttributeConverter.swift index b564e27bc..a7e04a54e 100644 --- a/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SuperscriptStringAttributeConverter.swift +++ b/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SuperscriptStringAttributeConverter.swift @@ -42,7 +42,7 @@ open class SuperscriptStringAttributeConverter: StringAttributeConverter { return false } - return baselineOffset.intValue > 0; + return baselineOffset.intValue >= 4; } } diff --git a/Aztec/Classes/Formatters/Implementations/SubscriptFormatter.swift b/Aztec/Classes/Formatters/Implementations/SubscriptFormatter.swift index 6bfd51b50..d200fae87 100644 --- a/Aztec/Classes/Formatters/Implementations/SubscriptFormatter.swift +++ b/Aztec/Classes/Formatters/Implementations/SubscriptFormatter.swift @@ -7,4 +7,26 @@ class SubscriptFormatter: StandardAttributeFormatter { attributeValue: NSNumber(-4), htmlRepresentationKey: .subHtmlRepresentation) } + + override func apply(to attributes: [NSAttributedString.Key: Any], andStore representation: HTMLRepresentation?) -> [NSAttributedString.Key: Any] { + var resultingAttributes = super.apply(to: attributes, andStore: representation) + guard let currentFont = attributes[.font] as? UIFont else { + return resultingAttributes + } + let font = UIFont(descriptor: currentFont.fontDescriptor, size: currentFont.pointSize - 2) + resultingAttributes[.font] = font + return resultingAttributes + } + + override func remove(from attributes: [NSAttributedString.Key: Any]) -> [NSAttributedString.Key: Any] { + var resultingAttributes = super.remove(from: attributes) + + guard let currentFont = attributes[.font] as? UIFont else { + return resultingAttributes + } + let font = UIFont(descriptor: currentFont.fontDescriptor, size: currentFont.pointSize + 2) + resultingAttributes[.font] = font + + return resultingAttributes + } } diff --git a/Aztec/Classes/Formatters/Implementations/SuperscriptFormatter.swift b/Aztec/Classes/Formatters/Implementations/SuperscriptFormatter.swift index 64424c025..f94594f85 100644 --- a/Aztec/Classes/Formatters/Implementations/SuperscriptFormatter.swift +++ b/Aztec/Classes/Formatters/Implementations/SuperscriptFormatter.swift @@ -7,4 +7,26 @@ class SuperscriptFormatter: StandardAttributeFormatter { attributeValue: NSNumber(4), htmlRepresentationKey: .supHtmlRepresentation) } + + override func apply(to attributes: [NSAttributedString.Key: Any], andStore representation: HTMLRepresentation?) -> [NSAttributedString.Key: Any] { + var resultingAttributes = super.apply(to: attributes, andStore: representation) + guard let currentFont = attributes[.font] as? UIFont else { + return resultingAttributes + } + let font = UIFont(descriptor: currentFont.fontDescriptor, size: currentFont.pointSize - 2) + resultingAttributes[.font] = font + return resultingAttributes + } + + override func remove(from attributes: [NSAttributedString.Key: Any]) -> [NSAttributedString.Key: Any] { + var resultingAttributes = super.remove(from: attributes) + + guard let currentFont = attributes[.font] as? UIFont else { + return resultingAttributes + } + let font = UIFont(descriptor: currentFont.fontDescriptor, size: currentFont.pointSize + 2) + resultingAttributes[.font] = font + + return resultingAttributes + } } From 3ab23d6620b0d3d3914366efd1fb8160c8fe90fc Mon Sep 17 00:00:00 2001 From: Sergio Estevao Date: Tue, 28 Apr 2020 15:54:49 +0100 Subject: [PATCH 07/11] Use zero has reference for superscript and subscript. --- .../Implementations/SubscriptStringAttributeConverter.swift | 2 +- .../Implementations/SuperscriptStringAttributeConverter.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SubscriptStringAttributeConverter.swift b/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SubscriptStringAttributeConverter.swift index d1914c5b9..cf83f59aa 100644 --- a/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SubscriptStringAttributeConverter.swift +++ b/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SubscriptStringAttributeConverter.swift @@ -42,7 +42,7 @@ open class SubscriptStringAttributeConverter: StringAttributeConverter { return false } - return baselineOffset.intValue <= -4; + return baselineOffset.intValue < 0; } } diff --git a/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SuperscriptStringAttributeConverter.swift b/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SuperscriptStringAttributeConverter.swift index a7e04a54e..b564e27bc 100644 --- a/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SuperscriptStringAttributeConverter.swift +++ b/Aztec/Classes/Converters/StringAttributesToAttributes/Implementations/SuperscriptStringAttributeConverter.swift @@ -42,7 +42,7 @@ open class SuperscriptStringAttributeConverter: StringAttributeConverter { return false } - return baselineOffset.intValue >= 4; + return baselineOffset.intValue > 0; } } From 472bed36b6a80077aaf3643034684b4e66e6dd89 Mon Sep 17 00:00:00 2001 From: Sergio Estevao Date: Wed, 29 Apr 2020 10:53:32 +0100 Subject: [PATCH 08/11] Move sub sup to another position. --- Example/Example/SampleContent/content.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Example/Example/SampleContent/content.html b/Example/Example/SampleContent/content.html index 510adf670..f2fe34588 100644 --- a/Example/Example/SampleContent/content.html +++ b/Example/Example/SampleContent/content.html @@ -10,13 +10,13 @@

Character Styles

Bold: Bold text
Italic: Italic text
+This is superscript(1) and this is subscript(2)
Underline: Underlined text
Strikethrough: Strikethrough
Colors: Royal Blue by name Red by hex

Underline with CSS: Alternative underline text
Links: I'm a link!
Open in new window link!
Code: print("Hello world")
-This is superscript(1) and this is subscript(2)
From 5e1b037b66e40814540a0e8b313f0f550291f03f Mon Sep 17 00:00:00 2001 From: Sergio Estevao Date: Wed, 29 Apr 2020 13:51:07 +0100 Subject: [PATCH 09/11] Add support for sup and sub tags. --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82ed39354..4411ee069 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +1.19.0 +------- +* Add support for the sup and sub HTML tags. + 1.18.0 ------- * Added an option to not colapse whitespaces when saving the HTML. From 8497f9e5e0d0b78e8653f344611f44ac429332c3 Mon Sep 17 00:00:00 2001 From: Sergio Estevao Date: Thu, 30 Apr 2020 22:46:48 +0100 Subject: [PATCH 10/11] Update version to 1.19.0 --- WordPress-Aztec-iOS.podspec | 2 +- WordPress-Editor-iOS.podspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/WordPress-Aztec-iOS.podspec b/WordPress-Aztec-iOS.podspec index 667ec5706..5d704b54b 100644 --- a/WordPress-Aztec-iOS.podspec +++ b/WordPress-Aztec-iOS.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'WordPress-Aztec-iOS' - s.version = '1.18.0' + s.version = '1.19.0' s.summary = 'The native HTML Editor.' # This description is used to generate tags and improve search results. diff --git a/WordPress-Editor-iOS.podspec b/WordPress-Editor-iOS.podspec index 71ca6ca80..e1ab31541 100644 --- a/WordPress-Editor-iOS.podspec +++ b/WordPress-Editor-iOS.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'WordPress-Editor-iOS' - s.version = '1.18.0' + s.version = '1.19.0' s.summary = 'The WordPress HTML Editor.' # This description is used to generate tags and improve search results. From fe24074a59ec139320242dc31ec29d2e8ff481a7 Mon Sep 17 00:00:00 2001 From: Sergio Estevao Date: Thu, 30 Apr 2020 22:46:58 +0100 Subject: [PATCH 11/11] Update changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4411ee069..08c53039c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 1.19.0 ------- * Add support for the sup and sub HTML tags. +* Fix invokation of the delegate method `shouldChangeTextIn` when pasting new content. 1.18.0 -------