diff --git a/README.md b/README.md index 90d77fe..5d403dc 100644 --- a/README.md +++ b/README.md @@ -268,6 +268,29 @@ To extend the tag name and customize its style, you can use the ExtendTagName cl let parser = ZHTMLParserBuilder.initWithDefault().add(ExtendTagName("zhgchgli"), withCustomStyle: MarkupStyle(backgroundColor: MarkupStyleColor(name: .aquamarine))).build() ``` +#### Paragraph Spacing Policy + +Often, you will want to create some type of spacing between various paragraphs or elements to increase legibility. There are two strategies to handle this: + +1. Create additional line breaks in between paragraphs and sections. This means that there will be the size of 1 empty paragraph between each other paragraph. This is the default behaviour. In this mode you will likely want to ensure you are ***Not*** using paragraph spacing in in your markup styles. +2. Make use of `paragraphSpacing` through `MarkupStyleParagraphStyle` or `NSParagraphStyle`. If you are using these you will likely want to disable the additional line breaks. + +This can be configured on your your `ZHTMLParserBuilder` as follows: + +```swift +let parser = ZHTMLParserBuilder + .initWithDefault() + // Indicate that we should use paragraph spacing for spacing. + .set(spacingPolicy: .paragraphSpacing) + // Create our spacing using paragraphSpacing. + .set(rootStyle: .init( + paragraphStyle: .init( + paragraphSpacing: 12 + ) + )) + .build() +``` + ### Render HTML String ```swift parser.render(htmlString) // NSAttributedString diff --git a/Sources/ZMarkupParser/Core/Processor/MarkupNSAttributedStringVisitor.swift b/Sources/ZMarkupParser/Core/Processor/MarkupNSAttributedStringVisitor.swift index 7f4fca2..7546b19 100644 --- a/Sources/ZMarkupParser/Core/Processor/MarkupNSAttributedStringVisitor.swift +++ b/Sources/ZMarkupParser/Core/Processor/MarkupNSAttributedStringVisitor.swift @@ -13,6 +13,7 @@ struct MarkupNSAttributedStringVisitor: MarkupVisitor { let components: [MarkupStyleComponent] let rootStyle: MarkupStyle? + var paragraphSpacingPolicy: ParagraphSpacingPolicy = .lineBreaks func visit(_ markup: RootMarkup) -> Result { return reduceBreaklineInResultNSAttributedString(collectAttributedString(markup)) @@ -82,14 +83,18 @@ struct MarkupNSAttributedStringVisitor: MarkupVisitor { func visit(_ markup: ListMarkup) -> Result { let attributedString = collectAttributedString(markup) - attributedString.append(makeBreakLine(in: markup)) + if paragraphSpacingPolicy == .lineBreaks { + attributedString.append(makeBreakLine(in: markup)) + } attributedString.insert(makeBreakLine(in: markup), at: 0) return attributedString } func visit(_ markup: ParagraphMarkup) -> Result { let attributedString = collectAttributedString(markup) - attributedString.append(makeBreakLine(in: markup, reduceable: false)) + if paragraphSpacingPolicy == .lineBreaks { + attributedString.append(makeBreakLine(in: markup, reduceable: false)) + } attributedString.insert(makeBreakLine(in: markup, reduceable: false), at: 0) return attributedString } @@ -143,14 +148,18 @@ struct MarkupNSAttributedStringVisitor: MarkupVisitor { func visit(_ markup: TableMarkup) -> Result { let attributedString = collectAttributedString(markup) - attributedString.append(makeBreakLine(in: markup)) + if paragraphSpacingPolicy == .lineBreaks { + attributedString.append(makeBreakLine(in: markup)) + } attributedString.insert(makeBreakLine(in: markup), at: 0) return attributedString } func visit(_ markup: HeadMarkup) -> NSAttributedString { let attributedString = collectAttributedString(markup) - attributedString.append(makeBreakLine(in: markup)) + if paragraphSpacingPolicy == .lineBreaks { + attributedString.append(makeBreakLine(in: markup)) + } attributedString.insert(makeBreakLine(in: markup), at: 0) return attributedString } @@ -163,7 +172,9 @@ struct MarkupNSAttributedStringVisitor: MarkupVisitor { func visit(_ markup: BlockQuoteMarkup) -> NSAttributedString { let attributedString = collectAttributedString(markup) - attributedString.append(makeBreakLine(in: markup)) + if paragraphSpacingPolicy == .lineBreaks { + attributedString.append(makeBreakLine(in: markup)) + } attributedString.insert(makeBreakLine(in: markup), at: 0) return attributedString } diff --git a/Sources/ZMarkupParser/Core/Processor/MarkupRenderProcessor.swift b/Sources/ZMarkupParser/Core/Processor/MarkupRenderProcessor.swift index 6783f32..102805d 100644 --- a/Sources/ZMarkupParser/Core/Processor/MarkupRenderProcessor.swift +++ b/Sources/ZMarkupParser/Core/Processor/MarkupRenderProcessor.swift @@ -7,17 +7,46 @@ import Foundation +public enum ParagraphSpacingPolicy { + /// + /// In this mode, line break will be used to create spacing between paragraphs + /// + /// For example + /// Line 1 + /// + /// Line 2 + case lineBreaks + /// + /// In this mode new additional line breaks are added. + /// + /// Instead the MarkupStyleParagraphStyle.paragraphSpacing will create spacing between paragraphs. + /// + /// For example + /// Line 1 + /// Line 2 + /// + case paragraphSpacing +} + + final class MarkupRenderProcessor: ParserProcessor { typealias From = (Markup, [MarkupStyleComponent]) typealias To = NSAttributedString let rootStyle: MarkupStyle? - init(rootStyle: MarkupStyle?) { + let paragraphSpacingPolicy: ParagraphSpacingPolicy + + init(rootStyle: MarkupStyle?, paragraphSpacingPolicy: ParagraphSpacingPolicy = .lineBreaks) { self.rootStyle = rootStyle + self.paragraphSpacingPolicy = paragraphSpacingPolicy } func process(from: From) -> To { - let visitor = MarkupNSAttributedStringVisitor(components: from.1, rootStyle: rootStyle) + let visitor = MarkupNSAttributedStringVisitor( + components: from.1, + rootStyle: rootStyle, + paragraphSpacingPolicy: paragraphSpacingPolicy + ) return visitor.visit(markup: from.0) } } diff --git a/Sources/ZMarkupParser/HTML/ZHTMLParser.swift b/Sources/ZMarkupParser/HTML/ZHTMLParser.swift index 97bf070..891442d 100644 --- a/Sources/ZMarkupParser/HTML/ZHTMLParser.swift +++ b/Sources/ZMarkupParser/HTML/ZHTMLParser.swift @@ -20,12 +20,18 @@ public final class ZHTMLParser { lazy var htmlStringToParsedResult: HTMLStringToParsedResultProcessor = HTMLStringToParsedResultProcessor() lazy var markupStripperProcessor: MarkupStripperProcessor = MarkupStripperProcessor() - init(htmlTags: [HTMLTag], styleAttributes: [HTMLTagStyleAttribute], policy: MarkupStylePolicy, rootStyle: MarkupStyle?) { + init( + htmlTags: [HTMLTag], + styleAttributes: [HTMLTagStyleAttribute], + policy: MarkupStylePolicy, + rootStyle: MarkupStyle?, + paragraphSpacingPolicy: ParagraphSpacingPolicy = .lineBreaks + ) { self.htmlTags = htmlTags self.styleAttributes = styleAttributes self.rootStyle = rootStyle - self.markupRenderProcessor = MarkupRenderProcessor(rootStyle: rootStyle) + self.markupRenderProcessor = MarkupRenderProcessor(rootStyle: rootStyle, paragraphSpacingPolicy: paragraphSpacingPolicy) self.htmlParsedResultToHTMLElementWithRootMarkupProcessor = HTMLParsedResultToHTMLElementWithRootMarkupProcessor(htmlTags: htmlTags) self.htmlElementWithMarkupToMarkupStyleProcessor = HTMLElementWithMarkupToMarkupStyleProcessor(styleAttributes: styleAttributes, policy: policy) diff --git a/Sources/ZMarkupParser/HTML/ZHTMLParserBuilder.swift b/Sources/ZMarkupParser/HTML/ZHTMLParserBuilder.swift index 2a4a7fa..ed7e0c9 100644 --- a/Sources/ZMarkupParser/HTML/ZHTMLParserBuilder.swift +++ b/Sources/ZMarkupParser/HTML/ZHTMLParserBuilder.swift @@ -13,6 +13,7 @@ public final class ZHTMLParserBuilder { private(set) var styleAttributes: [HTMLTagStyleAttribute] = [] private(set) var rootStyle: MarkupStyle? = .default private(set) var policy: MarkupStylePolicy = .respectMarkupStyleFromHTMLStyleAttribute + private(set) var paragraphSpacingPolicy: ParagraphSpacingPolicy = .lineBreaks public init() { @@ -63,7 +64,18 @@ public final class ZHTMLParserBuilder { return self } + public func set(spacingPolicy: ParagraphSpacingPolicy) -> Self { + self.paragraphSpacingPolicy = spacingPolicy + return self + } + public func build() -> ZHTMLParser { - return ZHTMLParser(htmlTags: htmlTags, styleAttributes: styleAttributes, policy: policy, rootStyle: rootStyle) + return ZHTMLParser( + htmlTags: htmlTags, + styleAttributes: styleAttributes, + policy: policy, + rootStyle: rootStyle, + paragraphSpacingPolicy: paragraphSpacingPolicy + ) } }