Skip to content

Commit

Permalink
Merge pull request #61 from ZhgChgLi/feature/adjust-list-item-tabs
Browse files Browse the repository at this point in the history
[Fix] HTML List(ul/ol) Typography issues
  • Loading branch information
zhgchgli0718 authored May 28, 2024
2 parents f1b1d9a + 1d56a2a commit 371b74f
Show file tree
Hide file tree
Showing 12 changed files with 82 additions and 34 deletions.
61 changes: 33 additions & 28 deletions Sources/ZMarkupParser/Core/MarkupStyle/MarkupStyleList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,25 @@ import AppKit

public struct MarkupStyleList {
let type: MarkupStyleType
let format: String
let headIndentMultiply: CGFloat
let indentMultiply: CGFloat
let startingItemNumber: Int

public init(type: MarkupStyleType, format: String, startingItemNumber: Int) {
/// {headIndentMultiply} - {indentMultiply} List Item 1
/// {headIndentMultiply} - {indentMultiply} List Item 2
/// mutiply base size is current font size
public init(type: MarkupStyleType = .disc, headIndentMultiply: CGFloat = 0.25, indentMultiply: CGFloat = 0.5, startingItemNumber: Int = 1) {
self.type = type
self.format = format
self.headIndentMultiply = headIndentMultiply
self.startingItemNumber = startingItemNumber
self.indentMultiply = indentMultiply
}

func marker(forItemNumber: Int) -> String {
return type.marker(forItemNumber: forItemNumber, startingItemNumber: startingItemNumber, format: format)
return type.marker(forItemNumber: forItemNumber, startingItemNumber: startingItemNumber)
}

// due to tab width caculator, we can't provide custom(String)
public enum MarkupStyleType {
case octal
case lowercaseAlpha
Expand Down Expand Up @@ -55,49 +61,48 @@ public struct MarkupStyleList {
}
}

func marker(forItemNumber: Int, startingItemNumber: Int, format: String) -> String {
let textList = NSTextList(markerFormat: self.markerFormat(), options: 0)
textList.startingItemNumber = startingItemNumber
return String(format: format, textList.marker(forItemNumber: forItemNumber))
}

private func markerFormat() -> NSTextList.MarkerFormat {
func marker(forItemNumber: Int, startingItemNumber: Int) -> String {
let textList: NSTextList
switch self {
case .octal:
return .octal
textList = NSTextList(markerFormat: .octal, options: 0)
case .lowercaseAlpha:
return .lowercaseAlpha
textList = NSTextList(markerFormat: .lowercaseAlpha, options: 0)
case .decimal:
return .decimal
textList = NSTextList(markerFormat: .decimal, options: 0)
case .lowercaseHexadecimal:
return .lowercaseHexadecimal
textList = NSTextList(markerFormat: .lowercaseHexadecimal, options: 0)
case .lowercaseLatin:
return .lowercaseLatin
textList = NSTextList(markerFormat: .lowercaseLatin, options: 0)
case .lowercaseRoman:
return .lowercaseRoman
textList = NSTextList(markerFormat: .lowercaseRoman, options: 0)
case .uppercaseAlpha:
return .uppercaseAlpha
textList = NSTextList(markerFormat: .uppercaseAlpha, options: 0)
case .uppercaseLatin:
return .uppercaseLatin
textList = NSTextList(markerFormat: .uppercaseLatin, options: 0)
case .uppercaseRoman:
return .uppercaseRoman
textList = NSTextList(markerFormat: .uppercaseRoman, options: 0)
case .uppercaseHexadecimal:
return .uppercaseHexadecimal
textList = NSTextList(markerFormat: .uppercaseHexadecimal, options: 0)
case .hyphen:
return .hyphen
textList = NSTextList(markerFormat: .hyphen, options: 0)
case .check:
return .check
textList = NSTextList(markerFormat: .check, options: 0)
case .circle:
return .circle
textList = NSTextList(markerFormat: .circle, options: 0)
case .disc:
return .disc
textList = NSTextList(markerFormat: .disc, options: 0)
case .diamond:
return .diamond
textList = NSTextList(markerFormat: .diamond, options: 0)
case .box:
return .box
textList = NSTextList(markerFormat: .box, options: 0)
case .square:
return .square
textList = NSTextList(markerFormat: .square, options: 0)
}

textList.startingItemNumber = startingItemNumber
let format = (isOrder()) ? ("\t%@.\t") : ("\t%@\t")
return String(format: format, textList.marker(forItemNumber: forItemNumber))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ struct MarkupNSAttributedStringVisitor: MarkupVisitor {
}
parentMarkup = parentMarkup?.parentMarkup
}
let indent = String(repeating: " ", count: level)
let indent = String(repeating: "\t", count: level - 1)

if let parentMarkup = markup.parentMarkup as? ListMarkup {
let thisAttributedString: NSMutableAttributedString
Expand All @@ -87,6 +87,14 @@ struct MarkupNSAttributedStringVisitor: MarkupVisitor {
} else {
thisAttributedString = NSMutableAttributedString(attributedString: makeString(in: markup, string:indent+parentMarkup.styleList.marker(forItemNumber: parentMarkup.styleList.startingItemNumber)))
}

// Since we use \t as an indentation character, the list text cannot contain \t, otherwise it will cause formatting issues, so we replace \t with spaces directly.
var tabRange = attributedString.mutableString.range(of: "\t", options: .caseInsensitive)
while tabRange.location != NSNotFound {
attributedString.replaceCharacters(in: tabRange, with: " ") // the default width of a \t character is typically equivalent to 8 spaces
tabRange = attributedString.mutableString.range(of: "\t", options: .caseInsensitive)
}

attributedString.insert(thisAttributedString, at: 0)
attributedString.markSuffixTagBoundaryBreakline()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
public struct OL_HTMLTagName: HTMLTagName {
public let string: String = WC3HTMLTagName.ol.rawValue
public let listStyle: MarkupStyleList
public init(listStyle: MarkupStyleList = MarkupStyleList(type: .decimal, format: "%@.", startingItemNumber: 1)) {
public init(listStyle: MarkupStyleList = MarkupStyleList(type: .decimal, startingItemNumber: 1)) {
self.listStyle = listStyle
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
public struct UL_HTMLTagName: HTMLTagName {
public let string: String = WC3HTMLTagName.ul.rawValue
public let listStyle: MarkupStyleList
public init(listStyle: MarkupStyleList = MarkupStyleList(type: .disc, format: "%@", startingItemNumber: 1)) {
public init(listStyle: MarkupStyleList = MarkupStyleList(type: .disc, startingItemNumber: 1)) {
self.listStyle = listStyle
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
//

import Foundation
#if canImport(UIKit)
import UIKit
#elseif canImport(AppKit)
import AppKit
#endif

public enum MarkupStylePolicy {
case respectMarkupStyleFromCode
Expand Down Expand Up @@ -70,7 +75,37 @@ struct HTMLElementMarkupComponentMarkupStyleVisitor: MarkupVisitor {
}

func visit(_ markup: ListMarkup) -> Result {
return defaultVisit(components.value(markup: markup))
var style = defaultVisit(components.value(markup: markup)) ?? MarkupStyle()

var level = 1
var parentMarkup: Markup? = markup.parentMarkup as? ListMarkup
while (parentMarkup != nil) {
if parentMarkup is ListMarkup {
level += 1
}
parentMarkup = parentMarkup?.parentMarkup
}

let headIndent = (style.font.size ?? 16) * markup.styleList.headIndentMultiply
let indent = (style.font.size ?? 16) * markup.styleList.indentMultiply

style.paragraphStyle.headIndent = CGFloat(level) * (style.paragraphStyle.headIndent ?? indent) + headIndent + (indent * CGFloat(level))

if style.paragraphStyle.tabStops == nil {
var tabStops: [NSTextTab] = [.init(textAlignment: .left, location: headIndent)]
for i in 1...level {
let dotWidth: CGFloat
if markup.styleList.type.isOrder() {
dotWidth = indent * CGFloat(i)
} else {
dotWidth = 0
}
tabStops.append(.init(textAlignment: .left, location: CGFloat(i) * indent + headIndent + dotWidth))
}
style.paragraphStyle.tabStops = tabStops
}

return style
}

func visit(_ markup: ParagraphMarkup) -> Result {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ final class MarkupNSAttributedStringVisitorTests: XCTestCase {
let rootMarkup = RootMarkup()
let paragraphMarkup_1 = ParagraphMarkup()
let paragraphMarkup_2 = ParagraphMarkup()
let listMarkup = ListMarkup(styleList: MarkupStyleList(type: .circle, format: "%@", startingItemNumber: 1))
let listMarkup = ListMarkup(styleList: MarkupStyleList(type: .circle, startingItemNumber: 1))
let listItemMarkup_1 = ListItemMarkup()
listItemMarkup_1.appendChild(markup: RawStringMarkup(attributedString: NSAttributedString(string: "11")))
let listItemMarkup_2 = ListItemMarkup()
Expand All @@ -89,7 +89,7 @@ final class MarkupNSAttributedStringVisitorTests: XCTestCase {

let result = visitor.visit(rootMarkup).string

XCTAssertEqual(result, " ◦11\n ◦22\n22\n ◦333\n\n", "Breakline reduce failed!")
XCTAssertEqual(result, "\t\t11\n\t\t22\n22\n\t\t333\n\n", "Breakline reduce failed!")
}


Expand Down

0 comments on commit 371b74f

Please sign in to comment.