Skip to content

Commit

Permalink
Fix HtmlAttributedStringConverterSpec edge case
Browse files Browse the repository at this point in the history
  • Loading branch information
mvasilak committed Aug 10, 2024
1 parent 407d74b commit 6ac4b69
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 3 deletions.
16 changes: 13 additions & 3 deletions Zotero/Controllers/HtmlAttributedStringConverter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,20 @@ final class HtmlAttributedStringConverter {
func convert(attributedString: NSAttributedString) -> String {
var attributes: [Attribute] = []

var previousRange: NSRange?
var activeAttributesAndRangeTuples: [([StringAttribute], NSRange)] = []
attributedString.enumerateAttributes(in: NSRange(location: 0, length: attributedString.length), options: []) { nsAttributes, range, _ in
let active = StringAttribute.attributes(from: nsAttributes)
if let previousTuple = activeAttributesAndRangeTuples.last, active == previousTuple.0, previousTuple.1.location + previousTuple.1.length == range.location {
let combinedRange = NSRange(location: previousTuple.1.location, length: previousTuple.1.length + range.length)
_ = activeAttributesAndRangeTuples.popLast()
activeAttributesAndRangeTuples.append((active, combinedRange))
} else {
activeAttributesAndRangeTuples.append((active, range))
}
}

var previousRange: NSRange?
for (active, range) in activeAttributesAndRangeTuples {
let currentIndex: Int
if let previousRange {
let previousIndex = attributes.first.flatMap { $0.index } ?? 0
Expand All @@ -32,8 +44,6 @@ final class HtmlAttributedStringConverter {
} else {
currentIndex = 0
}
// Currently active attributes
let active = StringAttribute.attributes(from: nsAttributes)
// Opened attributes so far
let opened = self.openedAttributes(from: attributes)
// Close opened attributes if they are not active anymore
Expand Down
53 changes: 53 additions & 0 deletions ZoteroTests/HtmlAttributedStringConverterSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,59 @@ final class HtmlAttributedStringConverterSpec: QuickSpec {
let string = htmlConverter.convert(attributedString: attributedString)
expect(string).to(equal(htmlStringRaw))
}

it("resets subscript by toggling with title font") {
// Edge case found during development, where there are consecutive ranges with the same attributes, due to the selected font.
let font = UIFont.preferredFont(for: .headline, weight: .regular)
let attributedStringRawParts = [
#"How"#,
#" "#,
#"Far"#,
#" Are We "#,
#"From"#,
#" AGI"#
]
let attributedStringRaw = attributedStringRawParts.joined()
var htmlStringRaw = ""
htmlStringRaw += "<sup>" + attributedStringRawParts[0] + "</sup>"
htmlStringRaw += attributedStringRawParts[1]
htmlStringRaw += "<sub>" + attributedStringRawParts[2] + "</sub>"
htmlStringRaw += attributedStringRawParts[3]
htmlStringRaw += "<b>" + attributedStringRawParts[4] + "</b>"
htmlStringRaw += attributedStringRawParts[5]
let subscriptRange = NSRange(
location: NSAttributedString(string: attributedStringRawParts[0] + attributedStringRawParts[1]).length,
length: NSAttributedString(string: attributedStringRawParts[2]).length
)
let attributesAndRanges: [([StringAttribute], NSRange)] = [
([.superscript], NSRange(location: 0, length: NSAttributedString(string: attributedStringRawParts[0]).length)),
([.subscript], subscriptRange),
([.bold], NSRange(
location: NSAttributedString(string: attributedStringRawParts[0] + attributedStringRawParts[1] + attributedStringRawParts[2] + attributedStringRawParts[3]).length,
length: NSAttributedString(string: attributedStringRawParts[4]).length
))
]
let attributedString = NSMutableAttributedString(string: attributedStringRaw, attributes: [.font: font])
expect(attributedString.string.count).to(equal(attributedStringRaw.count))
for (attributes, range) in attributesAndRanges {
attributedString.addAttributes(StringAttribute.nsStringAttributes(from: attributes, baseFont: font), range: range)
}
let string = htmlConverter.convert(attributedString: attributedString)
expect(string).to(equal(htmlStringRaw))
// Toggle subscript
var newHtmlStringRaw = ""
newHtmlStringRaw += "<sup>" + attributedStringRawParts[0] + "</sup>"
newHtmlStringRaw += attributedStringRawParts[1]
newHtmlStringRaw += attributedStringRawParts[2]
newHtmlStringRaw += attributedStringRawParts[3]
newHtmlStringRaw += "<b>" + attributedStringRawParts[4] + "</b>"
newHtmlStringRaw += attributedStringRawParts[5]
let newAttributedString = NSMutableAttributedString(attributedString: attributedString)
StringAttribute.toggleSubscript(in: newAttributedString, range: subscriptRange, defaultFont: font)
expect(newAttributedString.string.count).to(equal(attributedStringRaw.count))
let newString = htmlConverter.convert(attributedString: newAttributedString)
expect(newString).to(equal(newHtmlStringRaw))
}
}
}
}

0 comments on commit 6ac4b69

Please sign in to comment.