Created
August 7, 2024 14:46
-
-
Save fakiho/fa9a871bfc998052ca2358a940516bd8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Foundation | |
| import UIKit | |
| struct MarkdownParser { | |
| func parse(markdown: String) -> [AttributedString] { | |
| // Split the markdown string into lines to preserve newlines | |
| let lines = markdown.split(separator: "\n", omittingEmptySubsequences: false) | |
| var parsedLines = [AttributedString]() | |
| for line in lines { | |
| if let attributedLineString = try? AttributedString( | |
| markdown: String(line), | |
| options: .init(allowsExtendedAttributes: true, interpretedSyntax: .full) | |
| ) { | |
| parsedLines.append(attributedLineString) | |
| } | |
| } | |
| return parsedLines | |
| } | |
| } | |
| struct MarkdownHeaderFontStyle { | |
| var H1: UIFont | |
| var H2: UIFont | |
| var H3: UIFont | |
| } | |
| struct MarkdownConfiguration { | |
| var defaultFont: UIFont | |
| var boldFont: UIFont | |
| var italicFont: UIFont | |
| var boldItalicFont: UIFont | |
| var headerFontStyle: MarkdownHeaderFontStyle | |
| var textColor: UIColor | |
| var alignment: NSTextAlignment | |
| } | |
| class AttributedStringAssembler { | |
| private let configuration: MarkdownConfiguration | |
| init(configuration: MarkdownConfiguration) { | |
| self.configuration = configuration | |
| } | |
| func assemble(from parsedLines: [AttributedString]) -> NSAttributedString { | |
| let attributedString = NSMutableAttributedString() | |
| for attributedLineString in parsedLines { | |
| var lineString = attributedLineString | |
| // Apply default font and color | |
| lineString.font = configuration.defaultFont | |
| lineString.foregroundColor = configuration.textColor | |
| // Process runs for inline and presentation intents | |
| for run in lineString.runs { | |
| let range = run.range | |
| // Process inline presentation intents | |
| if let inlineIntent = run.attributes[AttributeScopes.FoundationAttributes.InlinePresentationIntentAttribute.self] { | |
| switch inlineIntent { | |
| case .emphasized: | |
| lineString[range].font = configuration.italicFont | |
| case .stronglyEmphasized: | |
| lineString[range].font = configuration.boldFont | |
| case [.stronglyEmphasized, .emphasized]: | |
| lineString[range].font = configuration.boldItalicFont | |
| default: | |
| break | |
| } | |
| } | |
| // Process presentation intents for headers and apply additional formatting | |
| if let presentationIntent = run.attributes[AttributeScopes.FoundationAttributes.PresentationIntentAttribute.self] { | |
| for component in presentationIntent.components { | |
| switch component.kind { | |
| case .header(level: let level): | |
| switch level { | |
| case 1: | |
| lineString[range].font = configuration.headerFontStyle.H1 | |
| case 2: | |
| lineString[range].font = configuration.headerFontStyle.H2 | |
| case 3: | |
| lineString[range].font = configuration.headerFontStyle.H3 | |
| default: | |
| lineString[range].font = configuration.headerFontStyle.H3 | |
| } | |
| default: | |
| break | |
| } | |
| } | |
| } | |
| } | |
| // Convert AttributedString to NSMutableAttributedString for further manipulation | |
| let mutableAttributedLineString = NSMutableAttributedString(attributedString: NSAttributedString(lineString)) | |
| // Add the processed line to the final attributed string | |
| attributedString.append(mutableAttributedLineString) | |
| // Append a newline character to preserve the original markdown newlines | |
| attributedString.append(NSAttributedString(string: "\n")) | |
| } | |
| // Apply paragraph style to the entire attributed string | |
| let paragraphStyle = NSMutableParagraphStyle() | |
| paragraphStyle.alignment = configuration.alignment | |
| let range = NSRange(location: 0, length: attributedString.length) | |
| attributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: range) | |
| return NSAttributedString(attributedString: attributedString) | |
| } | |
| } | |
| class MarkdownProcessor { | |
| private let parser: MarkdownParser | |
| private let assembler: AttributedStringAssembler | |
| init(configuration: MarkdownConfiguration) { | |
| self.parser = MarkdownParser() | |
| self.assembler = AttributedStringAssembler(configuration: configuration) | |
| } | |
| func process(markdown: String) -> NSAttributedString? { | |
| let parsedLines = parser.parse(markdown: markdown) | |
| return assembler.assemble(from: parsedLines) | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment