Skip to content

Commit

Permalink
Merge branch 'release/3.7.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
malcommac committed Jan 26, 2020
2 parents 7e7a70e + c480b71 commit 4c9122f
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 18 deletions.
5 changes: 3 additions & 2 deletions ExampleiOS/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ class ViewController: UIViewController {
fatalError()
}

self.textView?.attributedText = text.set(style: xmlStyle)
// self.textView?.attributedText = text.set(style: xmlStyle)

return
// return

// self.textView?.attributedText = "ciao ciao " + AttributedString(image: UIImage(named: "rocket")!,
// bounds: CGRect(x: 0, y: -20, width: 25, height: 25)) + "ciao ciao"
Expand Down Expand Up @@ -85,6 +85,7 @@ class ViewController: UIViewController {
"b": boldStyle,
"em": italicStyle,
"i": italicStyle,
"a": uppercasedRed,
"li": Style {
$0.paragraphSpacingBefore = self.baseFontSize / 2
$0.firstLineHeadIndent = self.baseFontSize
Expand Down
3 changes: 2 additions & 1 deletion Sources/SwiftRichString/Style/StyleGroup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ public class StyleXML: StyleProtocol {
public var baseStyle: StyleProtocol?

/// XML Parsing options.
public var xmlParsingOptions: XMLParsingOptions = []
/// By default `escapeString` is applied.
public var xmlParsingOptions: XMLParsingOptions = [.escapeString]

/// Image provider is called to provide custom image when `StyleXML` encounter a `img` tag image.
/// If not implemented the image is automatically searched inside any bundled `xcassets`.
Expand Down
48 changes: 48 additions & 0 deletions Sources/SwiftRichString/Support/Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,54 @@ import AppKit
import UIKit
#endif

extension String {

// Current implementation by @alexaubry in
// https://github.com/alexaubry/HTMLString
public func escapeWithUnicodeEntities() -> String {
var copy = self
copy.addUnicodeEntities()
return copy
}

internal mutating func addUnicodeEntities() {
var position: String.Index? = startIndex
let requiredEscapes: Set<Character> = ["!", "\"", "$", "%", "&", "'", "+", ",", "<", "=", ">", "@", "[", "]", "`", "{", "}"]

while let cursorPosition = position {
guard cursorPosition != endIndex else { break }
let character = self[cursorPosition]

if requiredEscapes.contains(character) {
// One of the required escapes for security reasons
let escape = "&#\(character.asciiValue!);" // required escapes can can only be ASCII
position = positionAfterReplacingCharacter(at: cursorPosition, with: escape)
} else {
// Not a required escape, no need to replace the character
position = index(cursorPosition, offsetBy: 1, limitedBy: endIndex)
}
}
}

/// Replaces the character at the given position with the escape and returns the new position.
fileprivate mutating func positionAfterReplacingCharacter(at position: String.Index, with escape: String) -> String.Index? {
let nextIndex = index(position, offsetBy: 1)

if let fittingPosition = index(position, offsetBy: escape.count, limitedBy: endIndex) {
// Check if we can fit the whole escape in the receiver
replaceSubrange(position ..< nextIndex, with: escape)
return fittingPosition
} else {
// If we can't, remove the character and insert the escape to make it fit.
remove(at: position)
insert(contentsOf: escape, at: position)
return index(position, offsetBy: escape.count, limitedBy: endIndex)
}
}


}

extension NSNumber {

internal static func from(float: Float?) -> NSNumber? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,22 +90,23 @@ extension XMLDynamicAttributesResolver {

open class StandardXMLAttributesResolver: XMLDynamicAttributesResolver {

public func applyDynamicAttributes(to attributedString: inout AttributedString, xmlStyle: XMLDynamicStyle, fromStyle: StyleXML) {
public init() {}

open func applyDynamicAttributes(to attributedString: inout AttributedString, xmlStyle: XMLDynamicStyle, fromStyle: StyleXML) {
let finalStyleToApply = Style()
xmlStyle.enumerateAttributes { key, value in
switch key {
case "color": // color support
finalStyleToApply.color = Color(hexString: value)

default:
break
default: break
}
}

self.styleForUnknownXMLTag(xmlStyle.tag, to: &attributedString, attributes: xmlStyle.xmlAttributes, fromStyle: fromStyle)
attributedString.add(style: finalStyleToApply)
}

public func styleForUnknownXMLTag(_ tag: String, to attributedString: inout AttributedString, attributes: [String: String]?, fromStyle: StyleXML) {
open func styleForUnknownXMLTag(_ tag: String, to attributedString: inout AttributedString, attributes: [String: String]?, fromStyle: StyleXML) {
let finalStyleToApply = Style()
switch tag {
case "a": // href support
Expand Down
9 changes: 8 additions & 1 deletion Sources/SwiftRichString/Support/XMLStringBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ public struct XMLParsingOptions: OptionSet {
/// recommended that you include a root node yourself and pass this option.
public static let doNotWrapXML = XMLParsingOptions(rawValue: 1)

/// Perform string escaping to replace all characters which is not supported by NSXMLParser
/// into the specified encoding with decimal entity.
/// For example if your string contains '&' character parser will break the style.
/// This option is active by default.
public static let escapeString = XMLParsingOptions(rawValue: 2)

}

// MARK: - XMLStringBuilder
Expand Down Expand Up @@ -104,7 +110,8 @@ public class XMLStringBuilder: NSObject, XMLParserDelegate {
public init(styleXML: StyleXML, string: String) {
self.styleXML = styleXML

let xml = (styleXML.xmlParsingOptions.contains(.doNotWrapXML) ? string : "<\(XMLStringBuilder.topTag)>\(string)</\(XMLStringBuilder.topTag)>")
let xmlString = (styleXML.xmlParsingOptions.contains(.escapeString) ? string.escapeWithUnicodeEntities() : string)
let xml = (styleXML.xmlParsingOptions.contains(.doNotWrapXML) ? xmlString : "<\(XMLStringBuilder.topTag)>\(xmlString)</\(XMLStringBuilder.topTag)>")
guard let data = xml.data(using: String.Encoding.utf8) else {
fatalError("Unable to convert to UTF8")
}
Expand Down
2 changes: 1 addition & 1 deletion SwiftRichString.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SwiftRichString"
s.version = "3.6.1"
s.version = "3.7.0"
s.summary = "Elegant Strings & Attributed Strings Toolkit for Swift"
s.description = <<-DESC
SwiftRichString is the best toolkit to work easily with Strings and Attributed Strings.
Expand Down
16 changes: 8 additions & 8 deletions SwiftRichString.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1640,7 +1640,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.11;
MARKETING_VERSION = 3.6.1;
MARKETING_VERSION = 3.7.0;
ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = "com.SwiftRichString.SwiftRichString-iOS";
PRODUCT_NAME = SwiftRichString;
Expand All @@ -1666,7 +1666,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.11;
MARKETING_VERSION = 3.6.1;
MARKETING_VERSION = 3.7.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.SwiftRichString.SwiftRichString-iOS";
PRODUCT_NAME = SwiftRichString;
SKIP_INSTALL = YES;
Expand Down Expand Up @@ -1718,7 +1718,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.11;
MARKETING_VERSION = 3.6.1;
MARKETING_VERSION = 3.7.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.SwiftRichString.SwiftRichString-watchOS";
PRODUCT_NAME = SwiftRichString;
SDKROOT = watchos;
Expand All @@ -1745,7 +1745,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.11;
MARKETING_VERSION = 3.6.1;
MARKETING_VERSION = 3.7.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.SwiftRichString.SwiftRichString-watchOS";
PRODUCT_NAME = SwiftRichString;
SDKROOT = watchos;
Expand All @@ -1772,7 +1772,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.11;
MARKETING_VERSION = 3.6.1;
MARKETING_VERSION = 3.7.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.SwiftRichString.SwiftRichString-tvOS";
PRODUCT_NAME = SwiftRichString;
SDKROOT = appletvos;
Expand All @@ -1799,7 +1799,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.11;
MARKETING_VERSION = 3.6.1;
MARKETING_VERSION = 3.7.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.SwiftRichString.SwiftRichString-tvOS";
PRODUCT_NAME = SwiftRichString;
SDKROOT = appletvos;
Expand Down Expand Up @@ -1828,7 +1828,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.11;
MARKETING_VERSION = 3.6.1;
MARKETING_VERSION = 3.7.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.SwiftRichString.SwiftRichString-macOS";
PRODUCT_NAME = SwiftRichString;
SDKROOT = macosx;
Expand All @@ -1855,7 +1855,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.11;
MARKETING_VERSION = 3.6.1;
MARKETING_VERSION = 3.7.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.SwiftRichString.SwiftRichString-macOS";
PRODUCT_NAME = SwiftRichString;
SDKROOT = macosx;
Expand Down

0 comments on commit 4c9122f

Please sign in to comment.