From 7784622ae17a57b37c1e0b9987811d57489c81eb Mon Sep 17 00:00:00 2001 From: ZhgChgLi <33706588+zhgchgli0718@users.noreply.github.com> Date: Fri, 14 Jun 2024 23:43:25 +0800 Subject: [PATCH 1/4] [Feat] add parse id and class html attribute --- .../HTML/HTMLTag/HTMLTagClassAttribute.swift | 22 ++++++++++ .../HTML/HTMLTag/HTMLTagIdAttribute.swift | 22 ++++++++++ ...entMarkupComponentMarkupStyleVisitor.swift | 42 +++++++++++++++++++ ...mentWithMarkupToMarkupStyleProcessor.swift | 9 +++- Sources/ZMarkupParser/HTML/ZHTMLParser.swift | 4 +- .../HTML/ZHTMLParserBuilder.swift | 24 +++++++++++ 6 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 Sources/ZMarkupParser/HTML/HTMLTag/HTMLTagClassAttribute.swift create mode 100644 Sources/ZMarkupParser/HTML/HTMLTag/HTMLTagIdAttribute.swift diff --git a/Sources/ZMarkupParser/HTML/HTMLTag/HTMLTagClassAttribute.swift b/Sources/ZMarkupParser/HTML/HTMLTag/HTMLTagClassAttribute.swift new file mode 100644 index 0000000..287173a --- /dev/null +++ b/Sources/ZMarkupParser/HTML/HTMLTag/HTMLTagClassAttribute.swift @@ -0,0 +1,22 @@ +// +// HTMLTagClassAttribute.swift +// +// +// Created by zhgchgli on 2024/6/14. +// + +import Foundation + +public struct HTMLTagClassAttribute { + public let className: String + public let render: (() -> (MarkupStyle?)) + + public init(className: String, render: @escaping (() -> (MarkupStyle?))) { + self.className = className + self.render = render + } + + func isEqualTo(className: String) -> Bool { + return self.className.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() == className.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() + } +} diff --git a/Sources/ZMarkupParser/HTML/HTMLTag/HTMLTagIdAttribute.swift b/Sources/ZMarkupParser/HTML/HTMLTag/HTMLTagIdAttribute.swift new file mode 100644 index 0000000..39517f4 --- /dev/null +++ b/Sources/ZMarkupParser/HTML/HTMLTag/HTMLTagIdAttribute.swift @@ -0,0 +1,22 @@ +// +// HTMLTagIdAttribute.swift +// +// +// Created by zhgchgli on 2024/6/14. +// + +import Foundation + +public struct HTMLTagIdAttribute { + public let idName: String + public let render: (() -> (MarkupStyle?)) + + public init(idName: String, render: @escaping (() -> (MarkupStyle?))) { + self.idName = idName + self.render = render + } + + func isEqualTo(idName: String) -> Bool { + return self.idName.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() == idName.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() + } +} diff --git a/Sources/ZMarkupParser/HTML/Processor/HTMLElementMarkupComponentMarkupStyleVisitor.swift b/Sources/ZMarkupParser/HTML/Processor/HTMLElementMarkupComponentMarkupStyleVisitor.swift index 165cc47..3139eb8 100644 --- a/Sources/ZMarkupParser/HTML/Processor/HTMLElementMarkupComponentMarkupStyleVisitor.swift +++ b/Sources/ZMarkupParser/HTML/Processor/HTMLElementMarkupComponentMarkupStyleVisitor.swift @@ -24,6 +24,9 @@ struct HTMLElementMarkupComponentMarkupStyleVisitor: MarkupVisitor { let policy: MarkupStylePolicy let components: [HTMLElementMarkupComponent] let styleAttributes: [HTMLTagStyleAttribute] + let classAttributes: [HTMLTagClassAttribute] + let idAttributes: [HTMLTagIdAttribute] + let rootStyle: MarkupStyle? func visit(_ markup: RootMarkup) -> Result { @@ -245,6 +248,45 @@ extension HTMLElementMarkupComponentMarkupStyleVisitor { markupStyle = customStyle } + // id + if let idString = htmlElement?.attributes?["id"], + let idAttribute = idAttributes.first(where: { $0.isEqualTo(idName: idString) }), + var thisMarkupStyle = idAttribute.render() { + switch policy { + case .respectMarkupStyleFromCode: + if var markupStyle = markupStyle { + markupStyle.fillIfNil(from: thisMarkupStyle) + } else { + markupStyle = thisMarkupStyle + } + case .respectMarkupStyleFromHTMLStyleAttribute: + thisMarkupStyle.fillIfNil(from: markupStyle ?? defaultStyle) + markupStyle = thisMarkupStyle + } + } + // class + if let classString = htmlElement?.attributes?["class"], + classAttributes.count > 0 { + let classNames = classString.split(separator: " ").filter { $0.trimmingCharacters(in: .whitespacesAndNewlines) != "" } + + for className in classNames { + if let classAttribute = classAttributes.first(where: { $0.isEqualTo(className: String(className)) }), + var thisMarkupStyle = classAttribute.render() { + switch policy { + case .respectMarkupStyleFromCode: + if var markupStyle = markupStyle { + markupStyle.fillIfNil(from: thisMarkupStyle) + } else { + markupStyle = thisMarkupStyle + } + case .respectMarkupStyleFromHTMLStyleAttribute: + thisMarkupStyle.fillIfNil(from: markupStyle ?? defaultStyle) + markupStyle = thisMarkupStyle + } + } + } + } + // style if let styleString = htmlElement?.attributes?["style"], styleAttributes.count > 0 { let styles = styleString.split(separator: ";").filter { $0.trimmingCharacters(in: .whitespacesAndNewlines) != "" }.map { $0.split(separator: ":") } diff --git a/Sources/ZMarkupParser/HTML/Processor/HTMLElementWithMarkupToMarkupStyleProcessor.swift b/Sources/ZMarkupParser/HTML/Processor/HTMLElementWithMarkupToMarkupStyleProcessor.swift index 19e391c..6fde2c6 100644 --- a/Sources/ZMarkupParser/HTML/Processor/HTMLElementWithMarkupToMarkupStyleProcessor.swift +++ b/Sources/ZMarkupParser/HTML/Processor/HTMLElementWithMarkupToMarkupStyleProcessor.swift @@ -12,17 +12,22 @@ final class HTMLElementWithMarkupToMarkupStyleProcessor: ParserProcessor { typealias To = [MarkupStyleComponent] let styleAttributes: [HTMLTagStyleAttribute] + let classAttributes: [HTMLTagClassAttribute] + let idAttributes: [HTMLTagIdAttribute] + let policy: MarkupStylePolicy let rootStyle: MarkupStyle? - init(styleAttributes: [HTMLTagStyleAttribute], policy: MarkupStylePolicy, rootStyle: MarkupStyle?) { + init(styleAttributes: [HTMLTagStyleAttribute], classAttributes: [HTMLTagClassAttribute], idAttributes: [HTMLTagIdAttribute], policy: MarkupStylePolicy, rootStyle: MarkupStyle?) { self.styleAttributes = styleAttributes + self.classAttributes = classAttributes + self.idAttributes = idAttributes self.policy = policy self.rootStyle = rootStyle } func process(from: From) -> To { var components: [MarkupStyleComponent] = [] - let visitor = HTMLElementMarkupComponentMarkupStyleVisitor(policy: policy, components: from.1, styleAttributes: styleAttributes, rootStyle: rootStyle) + let visitor = HTMLElementMarkupComponentMarkupStyleVisitor(policy: policy, components: from.1, styleAttributes: styleAttributes, classAttributes: classAttributes, idAttributes: idAttributes, rootStyle: rootStyle) walk(markup: from.0, visitor: visitor, components: &components) return components } diff --git a/Sources/ZMarkupParser/HTML/ZHTMLParser.swift b/Sources/ZMarkupParser/HTML/ZHTMLParser.swift index 3cebbaf..5ed6a84 100644 --- a/Sources/ZMarkupParser/HTML/ZHTMLParser.swift +++ b/Sources/ZMarkupParser/HTML/ZHTMLParser.swift @@ -23,6 +23,8 @@ public final class ZHTMLParser { init( htmlTags: [HTMLTag], styleAttributes: [HTMLTagStyleAttribute], + classAttributes: [HTMLTagClassAttribute], + idAttributes: [HTMLTagIdAttribute], policy: MarkupStylePolicy, rootStyle: MarkupStyle? ) { @@ -33,7 +35,7 @@ public final class ZHTMLParser { self.markupRenderProcessor = MarkupRenderProcessor(rootStyle: rootStyle) self.htmlParsedResultToHTMLElementWithRootMarkupProcessor = HTMLParsedResultToHTMLElementWithRootMarkupProcessor(htmlTags: htmlTags) - self.htmlElementWithMarkupToMarkupStyleProcessor = HTMLElementWithMarkupToMarkupStyleProcessor(styleAttributes: styleAttributes, policy: policy, rootStyle: rootStyle) + self.htmlElementWithMarkupToMarkupStyleProcessor = HTMLElementWithMarkupToMarkupStyleProcessor(styleAttributes: styleAttributes, classAttributes: classAttributes, idAttributes: idAttributes, policy: policy, rootStyle: rootStyle) } static let dispatchQueue: DispatchQueue = DispatchQueue(label: "ZHTMLParser.Queue") diff --git a/Sources/ZMarkupParser/HTML/ZHTMLParserBuilder.swift b/Sources/ZMarkupParser/HTML/ZHTMLParserBuilder.swift index b0bc4e7..1395225 100644 --- a/Sources/ZMarkupParser/HTML/ZHTMLParserBuilder.swift +++ b/Sources/ZMarkupParser/HTML/ZHTMLParserBuilder.swift @@ -11,6 +11,8 @@ public final class ZHTMLParserBuilder { private(set) var htmlTags: [HTMLTag] = [] private(set) var styleAttributes: [HTMLTagStyleAttribute] = [] + private(set) var classAttributes: [HTMLTagClassAttribute] = [] + private(set) var idAttributes: [HTMLTagIdAttribute] = [] private(set) var rootStyle: MarkupStyle? = .default private(set) var policy: MarkupStylePolicy = .respectMarkupStyleFromHTMLStyleAttribute @@ -53,6 +55,26 @@ public final class ZHTMLParserBuilder { return self } + public func add(_ classAttribute: HTMLTagClassAttribute) -> Self { + classAttributes.removeAll { thisAttribute in + return thisAttribute.className == classAttribute.className + } + + classAttributes.append(classAttribute) + + return self + } + + public func add(_ idAttribute: HTMLTagIdAttribute) -> Self { + idAttributes.removeAll { thisAttribute in + return thisAttribute.idName == idAttribute.idName + } + + idAttributes.append(idAttribute) + + return self + } + public func set(rootStyle: MarkupStyle) -> Self { self.rootStyle = rootStyle return self @@ -67,6 +89,8 @@ public final class ZHTMLParserBuilder { return ZHTMLParser( htmlTags: htmlTags, styleAttributes: styleAttributes, + classAttributes: classAttributes, + idAttributes: idAttributes, policy: policy, rootStyle: rootStyle ) From 29947604f5c344bdc2e3a7a950741dbb702f2cb2 Mon Sep 17 00:00:00 2001 From: ZhgChgLi <33706588+zhgchgli0718@users.noreply.github.com> Date: Fri, 14 Jun 2024 23:43:38 +0800 Subject: [PATCH 2/4] [Test] add id, class attribute tests --- ...ntMarkupComponentMarkupStyleVisitorTests.swift | 12 ++++++------ .../HTML/ZHTMLParserBuilderTests.swift | 15 +++++++++++++++ .../HTML/ZHTMLParserTests.swift | 2 +- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Tests/ZMarkupParserTests/HTML/HTMLElementMarkupComponentMarkupStyleVisitorTests.swift b/Tests/ZMarkupParserTests/HTML/HTMLElementMarkupComponentMarkupStyleVisitorTests.swift index c16c494..c3557db 100644 --- a/Tests/ZMarkupParserTests/HTML/HTMLElementMarkupComponentMarkupStyleVisitorTests.swift +++ b/Tests/ZMarkupParserTests/HTML/HTMLElementMarkupComponentMarkupStyleVisitorTests.swift @@ -12,7 +12,7 @@ import XCTest final class HTMLElementMarkupComponentMarkupStyleVisitorTests: XCTestCase { func testDefaultStyleByDefault() { - let visitor = HTMLElementMarkupComponentMarkupStyleVisitor(policy: .respectMarkupStyleFromCode, components: [], styleAttributes: [], rootStyle: nil) + let visitor = HTMLElementMarkupComponentMarkupStyleVisitor(policy: .respectMarkupStyleFromCode, components: [], styleAttributes: [], classAttributes: [], idAttributes: [], rootStyle: nil) let result = visitor.visit(markup: HeadMarkup(level: .h1)) XCTAssertEqual(result?.font.size, MarkupStyle.h1.font.size) @@ -21,7 +21,7 @@ final class HTMLElementMarkupComponentMarkupStyleVisitorTests: XCTestCase { func testDefaultStyleByCustomStyle() { let markup = InlineMarkup() let customStyle = MarkupStyle(font: .init(size: 99)) - let visitor = HTMLElementMarkupComponentMarkupStyleVisitor(policy: .respectMarkupStyleFromCode, components: [.init(markup: markup, value: .init(tag: .init(tagName: H1_HTMLTagName(), customStyle: customStyle), tagAttributedString: NSAttributedString(string: "test"), attributes: [:]))], styleAttributes: [], rootStyle: nil) + let visitor = HTMLElementMarkupComponentMarkupStyleVisitor(policy: .respectMarkupStyleFromCode, components: [.init(markup: markup, value: .init(tag: .init(tagName: H1_HTMLTagName(), customStyle: customStyle), tagAttributedString: NSAttributedString(string: "test"), attributes: [:]))], styleAttributes: [], classAttributes: [], idAttributes: [], rootStyle: nil) let result = visitor.visit(markup: markup) XCTAssertEqual(result?.font.size, customStyle.font.size) @@ -30,7 +30,7 @@ final class HTMLElementMarkupComponentMarkupStyleVisitorTests: XCTestCase { func testDefaultStyleShouldOverrideByCustomStyle() { let markup = HeadMarkup(level: .h1) let customStyle = MarkupStyle(font: .init(size: 99)) - let visitor = HTMLElementMarkupComponentMarkupStyleVisitor(policy: .respectMarkupStyleFromCode, components: [.init(markup: markup, value: .init(tag: .init(tagName: H1_HTMLTagName(), customStyle: customStyle), tagAttributedString: NSAttributedString(string: "