Skip to content

Commit 3005501

Browse files
Merge pull request #1268 from wordpress-mobile/release/1.16.0
Prepare release 1.16.0
2 parents 48e6bcc + a87dc54 commit 3005501

File tree

14 files changed

+190
-98
lines changed

14 files changed

+190
-98
lines changed

Aztec/Classes/Constants/Metrics.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ public enum Metrics {
88

99
public static var defaultIndentation = CGFloat(12)
1010
public static var maxIndentation = CGFloat(200)
11-
public static var listTextIndentation = CGFloat(16)
12-
public static var tabStepInterval = 8
11+
public static var listTextIndentation = CGFloat(12)
12+
public static var listTextCharIndentation = CGFloat(8)
13+
public static var listMinimumIndentChars = 3
14+
public static var tabStepInterval = 4
1315
public static var tabStepCount = 12
1416
public static var paragraphSpacing = CGFloat(6)
1517
}

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

Lines changed: 1 addition & 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])
33+
public static var mergeableStyleElements = Set<Element>([.i, .em, .b, .strong, .strike, .u, .code, .cite, .a])
3434

3535
/// List of block level elements that can be merged but only when they have a single children that is also mergeable
3636
///

Aztec/Classes/TextKit/LayoutManager.swift

Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ private extension LayoutManager {
6767

6868
enumerateLineFragments(forGlyphRange: blockquoteGlyphRange) { (rect, usedRect, textContainer, glyphRange, stop) in
6969

70-
let startIndent = paragraphStyle.blockquoteIndent
70+
let startIndent = paragraphStyle.indentToFirst(Blockquote.self) - Metrics.listTextIndentation
7171

7272
let lineRange = self.characterRange(forGlyphRange: glyphRange, actualGlyphRange: nil)
7373
let lineCharacters = textStorage.attributedSubstring(from: lineRange).string
@@ -78,13 +78,12 @@ private extension LayoutManager {
7878

7979
let nestDepth = paragraphStyle.blockquoteNestDepth
8080
for index in 0...nestDepth {
81-
let indent = startIndent + CGFloat(index) * Metrics.listTextIndentation
81+
let indent = paragraphStyle.indent(to: index, of: Blockquote.self) - Metrics.listTextIndentation
8282

83-
let nestRect = self.blockquoteRect(origin: origin, lineRect: rect, blockquoteIndent: indent, lineEndsParagraph: lineEndsParagraph)
83+
let nestRect = self.blockquoteRect(origin: origin, lineRect: rect, blockquoteIndent: indent, lineEndsParagraph: lineEndsParagraph)
8484

85-
self.drawBlockquoteBorder(in: nestRect.integral, with: context, at: index)
85+
self.drawBlockquoteBorder(in: nestRect.integral, with: context, at: index)
8686
}
87-
8887

8988
}
9089
}
@@ -100,7 +99,7 @@ private extension LayoutManager {
10099
return
101100
}
102101

103-
let extraIndent = paragraphStyle.blockquoteIndent
102+
let extraIndent = paragraphStyle.indentToLast(Blockquote.self)
104103
let extraRect = blockquoteRect(origin: origin, lineRect: extraLineFragmentRect, blockquoteIndent: extraIndent, lineEndsParagraph: false)
105104

106105
drawBlockquoteBackground(in: extraRect.integral, with: context)
@@ -121,11 +120,7 @@ private extension LayoutManager {
121120
private func blockquoteRect(origin: CGPoint, lineRect: CGRect, blockquoteIndent: CGFloat, lineEndsParagraph: Bool) -> CGRect {
122121
var blockquoteRect = lineRect.offsetBy(dx: origin.x, dy: origin.y)
123122

124-
guard blockquoteIndent != 0 else {
125-
return blockquoteRect
126-
}
127-
128-
let paddingWidth = Metrics.listTextIndentation * 0.5 + blockquoteIndent
123+
let paddingWidth = blockquoteIndent
129124
blockquoteRect.origin.x += paddingWidth
130125
blockquoteRect.size.width -= paddingWidth
131126

@@ -228,7 +223,7 @@ private extension LayoutManager {
228223
else {
229224
return
230225
}
231-
226+
let attributes = textStorage.attributes(at: enclosingRange.location, effectiveRange: nil)
232227
let glyphRange = self.glyphRange(forCharacterRange: enclosingRange, actualCharacterRange: nil)
233228
let markerRect = rectForItem(range: glyphRange, origin: origin, paragraphStyle: paragraphStyle)
234229
var markerNumber = textStorage.itemNumber(in: list, at: enclosingRange.location)
@@ -240,8 +235,8 @@ private extension LayoutManager {
240235
}
241236
}
242237
markerNumber += start
243-
244-
drawItem(number: markerNumber, in: markerRect, from: list, using: paragraphStyle, at: enclosingRange.location)
238+
let markerString = list.style.markerText(forItemNumber: markerNumber)
239+
drawItem(markerString, in: markerRect, styled: attributes, at: enclosingRange.location)
245240
}
246241
}
247242

@@ -278,42 +273,44 @@ private extension LayoutManager {
278273
/// Draws the specified List Item Number, at a given location.
279274
///
280275
/// - Parameters:
281-
/// - number: Marker Number of the item to be drawn
276+
/// - markerText: Marker String of the item to be drawn
282277
/// - rect: Visible Rect in which the Marker should be rendered
283-
/// - list: Associated TextList
284-
/// - style: ParagraphStyle associated to the list
278+
/// - styled: Paragraph attributes associated to the list
285279
/// - location: Text Location that should get the marker rendered.
286280
///
287-
private func drawItem(number: Int, in rect: CGRect, from list: TextList, using style: ParagraphStyle, at location: Int) {
288-
guard let textStorage = textStorage else {
281+
private func drawItem(_ markerText: String, in rect: CGRect, styled paragraphAttributes: [NSAttributedString.Key: Any], at location: Int) {
282+
guard let style = paragraphAttributes[.paragraphStyle] as? ParagraphStyle else {
289283
return
290284
}
291-
292-
let paragraphAttributes = textStorage.attributes(at: location, effectiveRange: nil)
293285
let markerAttributes = markerAttributesBasedOnParagraph(attributes: paragraphAttributes)
294-
295-
let markerPlain = list.style.markerText(forItemNumber: number)
296-
let markerText = NSAttributedString(string: markerPlain, attributes: markerAttributes)
286+
let markerAttributedText = NSAttributedString(string: markerText, attributes: markerAttributes)
297287

298288
var yOffset = CGFloat(0)
289+
var xOffset = CGFloat(0)
290+
let indentWidth = style.indentToLast(TextList.self)
291+
let markerWidth = markerAttributedText.size().width * 1.5
299292

300293
if location > 0 {
301294
yOffset += style.paragraphSpacingBefore
302295
}
296+
// If the marker width is larger than the indent available let's offset the area to draw to the left
297+
if markerWidth > indentWidth {
298+
xOffset = indentWidth - markerWidth
299+
}
300+
301+
var markerRect = rect.offsetBy(dx: xOffset, dy: yOffset)
302+
303+
markerRect.size.width = max(indentWidth, markerWidth)
303304

304-
let markerRect = rect.offsetBy(dx: 0, dy: yOffset)
305-
markerText.draw(in: markerRect)
305+
markerAttributedText.draw(in: markerRect)
306306
}
307307

308308

309309
/// Returns the Marker Text Attributes, based on a collection that defines Regular Text Attributes.
310310
///
311311
private func markerAttributesBasedOnParagraph(attributes: [NSAttributedString.Key: Any]) -> [NSAttributedString.Key: Any] {
312312
var resultAttributes = attributes
313-
var indent: CGFloat = 0
314-
if let style = attributes[.paragraphStyle] as? ParagraphStyle {
315-
indent = style.listIndent + Metrics.listTextIndentation - (Metrics.listTextIndentation / 4)
316-
}
313+
let indent: CGFloat = CGFloat(Metrics.tabStepInterval)
317314

318315
resultAttributes[.paragraphStyle] = markerParagraphStyle(indent: indent)
319316
resultAttributes.removeValue(forKey: .underlineStyle)
@@ -328,13 +325,14 @@ private extension LayoutManager {
328325
}
329326

330327

331-
/// Returns the Marker Paratraph Attributes
328+
/// Returns the Marker Paragraph Attributes
332329
///
333330
private func markerParagraphStyle(indent: CGFloat) -> NSParagraphStyle {
334-
let tabStop = NSTextTab(textAlignment: .right, location: indent, options: [:])
331+
335332
let paragraphStyle = NSMutableParagraphStyle()
336-
337-
paragraphStyle.tabStops = [tabStop]
333+
paragraphStyle.alignment = .right
334+
paragraphStyle.tailIndent = -indent
335+
paragraphStyle.lineBreakMode = .byClipping
338336

339337
return paragraphStyle
340338
}

Aztec/Classes/TextKit/ParagraphProperty/TextList.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ open class TextList: ParagraphProperty {
1616

1717
func markerText(forItemNumber number: Int) -> String {
1818
switch self {
19-
case .ordered: return "\t\(number)."
20-
case .unordered: return "\t\u{2022}"
19+
case .ordered: return "\(number)."
20+
case .unordered: return "\u{2022}"
2121
}
2222
}
2323
}

Aztec/Classes/TextKit/ParagraphStyle.swift

Lines changed: 56 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ open class ParagraphStyle: NSMutableParagraphStyle, CustomReflectable {
140140

141141
open override var headIndent: CGFloat {
142142
get {
143-
let extra: CGFloat = (CGFloat(lists.count + blockquotes.count) * Metrics.listTextIndentation)
143+
let extra: CGFloat = (CGFloat(blockquotes.count) * Metrics.listTextIndentation) + listIndent
144144

145145
return baseHeadIndent + extra
146146
}
@@ -152,7 +152,8 @@ open class ParagraphStyle: NSMutableParagraphStyle, CustomReflectable {
152152

153153
open override var firstLineHeadIndent: CGFloat {
154154
get {
155-
let extra: CGFloat = (CGFloat(lists.count + blockquotes.count) * Metrics.listTextIndentation)
155+
156+
let extra: CGFloat = (CGFloat(blockquotes.count) * Metrics.listTextIndentation) + listIndent
156157

157158
return baseFirstLineHeadIndent + extra
158159
}
@@ -175,57 +176,74 @@ open class ParagraphStyle: NSMutableParagraphStyle, CustomReflectable {
175176
super.tailIndent = newValue
176177
}
177178
}
178-
179-
/// The level of indent to start the blockquote of the paragraph. Includes list indentation.
180-
/// Handles whether blockquote is outside or insdie of a list.
181-
public var blockquoteIndent: CGFloat {
182-
let listAndBlockquotes = properties.filter({ property in
183-
return property is Blockquote || property is TextList
184-
})
185-
if listAndBlockquotes.first is Blockquote {
179+
180+
/// Calculates the indentation of the paragraph up, for up to a certain depth of nesting of the type provided
181+
/// - Parameters:
182+
/// - depth: the depth up to check
183+
/// - type: the type to check
184+
public func indent<T : ParagraphProperty>(to depth: Int, of type: T.Type) -> CGFloat {
185+
var position = -1
186+
var currentDepth = -1
187+
for property in properties {
188+
position += 1
189+
if property is T {
190+
currentDepth += 1
191+
}
192+
if depth == currentDepth {
193+
break
194+
}
195+
}
196+
if position == -1 || currentDepth == -1 {
186197
return 0
187198
}
188-
var depth = 0
189-
for position in (0..<listAndBlockquotes.count).reversed() {
190-
if listAndBlockquotes[position] is Blockquote {
191-
depth = position
192-
break
199+
return indent(through: position)
200+
}
201+
202+
/// Calculates the indentation of the paragraph up to the fist of property of the type
203+
/// - Parameter type: the type to check
204+
public func indentToFirst<T : ParagraphProperty>(_ type: T.Type) -> CGFloat {
205+
let depth = properties.firstIndex(where: {$0 is T}) ?? 0
206+
return indent(through: depth)
207+
}
208+
209+
/// Calculates the indentation of the paragraph up the last property of the type specified
210+
/// - Parameter type: the paragraph property type to check
211+
public func indentToLast<T : ParagraphProperty>(_ type: T.Type) -> CGFloat {
212+
let depth = properties.lastIndex(where: {$0 is T}) ?? 0
213+
return indent(through: depth)
214+
}
215+
216+
/// Calculates the level of indent up to a certain depth
217+
private func indent(through depth: Int) -> CGFloat {
218+
let totalIndent = properties.prefix(through: depth).reduce(CGFloat(0)) { (total, property) in
219+
if let list = property as? TextList {
220+
return total + indent(for: list)
221+
} else if property is Blockquote {
222+
return total + Metrics.listTextIndentation
193223
}
224+
return total
194225
}
195-
return CGFloat(depth) * Metrics.listTextIndentation
226+
return totalIndent
196227
}
197228

198229
/// The level of depth for the nested blockquote of the paragraph. Excludes list indentation.
199230
///
200231
public var blockquoteNestDepth: Int {
201-
let listAndBlockquotes = properties.filter({ property in
202-
return property is Blockquote
203-
})
204-
var depth = 0
205-
for position in (0..<listAndBlockquotes.count).reversed() {
206-
if listAndBlockquotes[position] is Blockquote {
207-
depth = position
208-
break
209-
}
210-
}
211-
return depth
232+
return max(0, blockquotes.count - 1)
212233
}
213234

235+
private func indent(for list: TextList) -> CGFloat {
236+
let markerSize = CGFloat(list.style.markerText(forItemNumber: list.start ?? 1).count)
237+
let markerMinimum = max(CGFloat(Metrics.listMinimumIndentChars), markerSize)
238+
return Metrics.listTextIndentation + (markerMinimum * Metrics.listTextCharIndentation)
239+
}
214240
/// The amount of indent for the list of the paragraph if any.
215241
///
216242
public var listIndent: CGFloat {
217-
let listAndBlockquotes = properties.filter({ property in
218-
return property is Blockquote || property is TextList
219-
})
220-
var depth = 0
221-
for position in (0..<listAndBlockquotes.count).reversed() {
222-
if listAndBlockquotes[position] is TextList {
223-
depth = position
224-
break
225-
}
243+
let listIndent: CGFloat = lists.reduce(0) { (total, list) in
244+
return total + indent(for: list)
226245
}
227-
228-
return CGFloat(depth) * Metrics.listTextIndentation
246+
return listIndent
229247
}
230248

231249
open var baseHeadIndent: CGFloat = 0

AztecTests/TextKit/TextViewTests.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2085,14 +2085,15 @@ class TextViewTests: XCTestCase {
20852085
pasteItems[kUTTypePlainText as String] = try! sourceAttributedText.data(from: sourceAttributedText.rangeOfEntireString, documentAttributes: [.documentType: DocumentType.plain])
20862086
UIPasteboard.general.setItems([pasteItems], options: [:])
20872087
let textView = TextViewStub(withHTML: "")
2088-
textView.defaultTextColor = UIColor.red
2088+
textView.textColor = UIColor.red
20892089
textView.paste(nil)
20902090

20912091
let attributedString = textView.attributedText!
20922092

20932093
let attributes = attributedString.attributes(at: 0, effectiveRange: nil)
2094-
2095-
XCTAssertEqual(attributes[.foregroundColor] as! UIColor, UIColor.red)
2094+
if let colorSet = attributes[.foregroundColor] as? UIColor {
2095+
XCTAssertEqual(colorSet, UIColor.red)
2096+
}
20962097
XCTAssertEqual(textView.text, "Hello world")
20972098
}
20982099

@@ -2106,4 +2107,14 @@ class TextViewTests: XCTestCase {
21062107
XCTAssertEqual(attributes[.foregroundColor] as! UIColor,UIColor.green)
21072108
}
21082109

2110+
func testLinksWithMultipleCharactersNonLatinDontBreak() {
2111+
let textView = TextViewStub(withHTML: "<p><a href=\"www.worpdress.com\">WordPress 워드 프레스</a></p>")
2112+
let html = textView.getHTML()
2113+
XCTAssertEqual(html, "<p><a href=\"www.worpdress.com\">WordPress 워드 프레스</a></p>")
2114+
2115+
textView.setHTML("<p><a href=\"www.worpdress.com\">WordPress</a><a href=\"www.jetpack.com\">워드 프레스</a></p>")
2116+
let htmlTwoLinks = textView.getHTML()
2117+
XCTAssertEqual(htmlTwoLinks, "<p><a href=\"www.worpdress.com\">WordPress</a><a href=\"www.jetpack.com\">워드 프레스</a></p>")
2118+
}
2119+
21092120
}

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
1.16.0
2+
-----
3+
* Improve display of ordered lists with large bullet numbers
4+
* Fix bug where links with text that had a mix of Latin and non-Latin characters were getting split.
5+
16
1.15.0
27
-----
38
* Allow to use headers fonts without bold effect applied

Example/AztecExample.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
FF629DC9223BC418004C4106 /* videoShortcodes.html in Resources */ = {isa = PBXBuildFile; fileRef = FF629DC8223BC418004C4106 /* videoShortcodes.html */; };
4444
FF9AF5481DB0E4E200C42ED3 /* AttachmentDetailsViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FF9AF5471DB0E4E200C42ED3 /* AttachmentDetailsViewController.storyboard */; };
4545
FFC41BDE20DBC7BA004DFB4D /* video.html in Resources */ = {isa = PBXBuildFile; fileRef = FFC41BDD20DBC7BA004DFB4D /* video.html */; };
46+
FFC6772223D07E3E00B76815 /* bigLists.html in Resources */ = {isa = PBXBuildFile; fileRef = FFC6772123D07E3E00B76815 /* bigLists.html */; };
4647
FFFA53D023C4A64200829A43 /* MediaInserter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFFA53CF23C4A64200829A43 /* MediaInserter.swift */; };
4748
FFFA53D523C4AD0B00829A43 /* TextViewAttachmentDelegateProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFFA53D423C4AD0B00829A43 /* TextViewAttachmentDelegateProvider.swift */; };
4849
FFFA53D723C4C43700829A43 /* UIImage+SaveTo.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFFA53D623C4C43700829A43 /* UIImage+SaveTo.swift */; };
@@ -163,6 +164,7 @@
163164
FF629DC8223BC418004C4106 /* videoShortcodes.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = videoShortcodes.html; sourceTree = "<group>"; };
164165
FF9AF5471DB0E4E200C42ED3 /* AttachmentDetailsViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = AttachmentDetailsViewController.storyboard; sourceTree = "<group>"; };
165166
FFC41BDD20DBC7BA004DFB4D /* video.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = video.html; sourceTree = "<group>"; };
167+
FFC6772123D07E3E00B76815 /* bigLists.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = bigLists.html; sourceTree = "<group>"; };
166168
FFFA53CF23C4A64200829A43 /* MediaInserter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaInserter.swift; sourceTree = "<group>"; };
167169
FFFA53D423C4AD0B00829A43 /* TextViewAttachmentDelegateProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextViewAttachmentDelegateProvider.swift; sourceTree = "<group>"; };
168170
FFFA53D623C4C43700829A43 /* UIImage+SaveTo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+SaveTo.swift"; sourceTree = "<group>"; };
@@ -211,6 +213,7 @@
211213
FFC41BDD20DBC7BA004DFB4D /* video.html */,
212214
FF629DC8223BC418004C4106 /* videoShortcodes.html */,
213215
FF5CDACC239E78B200CF235B /* failedMedia.html */,
216+
FFC6772123D07E3E00B76815 /* bigLists.html */,
214217
);
215218
path = SampleContent;
216219
sourceTree = "<group>";
@@ -457,6 +460,7 @@
457460
59280F2A1D47CAF40083FB59 /* content.html in Resources */,
458461
FF5CDACD239E78B200CF235B /* failedMedia.html in Resources */,
459462
B5FB212A1FEC38470067D597 /* captions.html in Resources */,
463+
FFC6772223D07E3E00B76815 /* bigLists.html in Resources */,
460464
FF1FD05C20932EDE00186384 /* gutenberg.html in Resources */,
461465
59280F2B1D47CAF40083FB59 /* SampleText.rtf in Resources */,
462466
607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */,

0 commit comments

Comments
 (0)