{"id":1751,"url":"https://github.com/ZhgChgLi/ZMarkupParser","last_synced_at":"2025-08-02T04:32:32.372Z","repository":{"id":65940228,"uuid":"602927147","full_name":"ZhgChgLi/ZMarkupParser","owner":"ZhgChgLi","description":"ZMarkupParser is a pure-Swift library that helps you convert HTML strings into NSAttributedString with customized styles and tags.","archived":false,"fork":false,"pushed_at":"2025-06-01T03:23:02.000Z","size":36451,"stargazers_count":347,"open_issues_count":5,"forks_count":28,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-07-29T21:34:59.353Z","etag":null,"topics":["cocoapods","html","html-converter","html-parser","html-renderer","ios","nsattributedstring","swift","swift-package","textfield","uikit","uilabel","uitextview"],"latest_commit_sha":null,"homepage":"https://link.zhgchg.li/","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ZhgChgLi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"buy_me_a_coffee":"zhgchgli"}},"created_at":"2023-02-17T08:41:37.000Z","updated_at":"2025-07-22T10:13:16.000Z","dependencies_parsed_at":"2024-01-07T22:23:39.890Z","dependency_job_id":"f505ac86-1429-475f-ab35-3755b4ab47f5","html_url":"https://github.com/ZhgChgLi/ZMarkupParser","commit_stats":{"total_commits":164,"total_committers":4,"mean_commits":41.0,"dds":"0.41463414634146345","last_synced_commit":"f530d79ed2677cdebb24015c86ceac50b12588b3"},"previous_names":[],"tags_count":51,"template":false,"template_full_name":null,"purl":"pkg:github/ZhgChgLi/ZMarkupParser","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZhgChgLi%2FZMarkupParser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZhgChgLi%2FZMarkupParser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZhgChgLi%2FZMarkupParser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZhgChgLi%2FZMarkupParser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ZhgChgLi","download_url":"https://codeload.github.com/ZhgChgLi/ZMarkupParser/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZhgChgLi%2FZMarkupParser/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267772844,"owners_count":24142095,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-29T02:00:12.549Z","response_time":2574,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cocoapods","html","html-converter","html-parser","html-renderer","ios","nsattributedstring","swift","swift-package","textfield","uikit","uilabel","uitextview"],"created_at":"2024-01-05T20:15:54.925Z","updated_at":"2025-08-02T04:32:32.362Z","avatar_url":"https://github.com/ZhgChgLi.png","language":"Swift","funding_links":["https://buymeacoffee.com/zhgchgli","https://www.buymeacoffee.com/zhgchgli","https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20beer!\u0026emoji=%F0%9F%8D%BA\u0026slug=zhgchgli\u0026button_colour=FFDD00\u0026font_colour=000000\u0026font_family=Bree\u0026outline_colour=000000\u0026coffee_colour=ffffff"],"categories":["Parsing","Libs"],"sub_categories":["XML \u0026 HTML","Network"],"readme":"![ZMarkupParser](https://user-images.githubusercontent.com/33706588/219608966-20e0c017-d05c-433a-9a52-091bc0cfd403.jpg)\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://codecov.io/gh/ZhgChgLi/ZMarkupParser\" target=\"_blank\"\u003e\u003cimg src=\"https://codecov.io/gh/ZhgChgLi/ZMarkupParser/branch/main/graph/badge.svg?token=MPzgO1tnr9\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/ZhgChgLi/ZMarkupParser/actions/workflows/ci.yml\" target=\"_blank\"\u003e\u003cimg src=\"https://github.com/ZhgChgLi/ZMarkupParser/actions/workflows/ci.yml/badge.svg?branch=main\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/vsouza/awesome-ios/tree/master#xml--html\" target=\"_blank\"\u003e\u003cimg alt=\"awesome\" src=\"https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nZMarkupParser is a pure-Swift library that helps you convert HTML strings into NSAttributedString with customized styles and tags.\n\n## Technical Details:\n- \\[Chinese\\] [Development Notes](https://medium.com/zrealm-ios-dev/zmarkupparser-html-string-%E8%BD%89%E6%8F%9B-nsattributedstring-%E5%B7%A5%E5%85%B7-a5643de271e4)\n- \\[English\\] [Development Notes (translated using ChatGPT)](https://en.zhgchg.li/posts/2724f02f6e7/)\n- \\[English\\] [Brief Introduction to Operation](https://github.com/ZhgChgLi/ZMarkupParser/blob/main/technicalDetail.md)\n\n## Features\n- [x] Parse HTML strings using pure-Swift and regular expressions.\n- [x] Automatically correct invalid HTML strings, including mixed or isolated tags (e.g., `\u003ca\u003eLink\u003cb\u003eLinkBold\u003c/a\u003eBold\u003c/b\u003e\u003cbr\u003e` -\u003e `\u003ca\u003eLink\u003cb\u003eLinkBold\u003c/b\u003e\u003c/a\u003e\u003cb\u003eBold\u003c/b\u003e\u003cbr/\u003e`).\n- [x] More compatible with HTML tags than a parser that is based on XMLParser.\n- [x] Customizable HTML tag parser with painless extended tag support and the ability to customize tag styles.\n- [x] Support for HTML rendering, stripping, and selecting.\n- [x] Support for `\u003cul\u003e` list views, `\u003ctable\u003e` table view, `\u003cimg\u003e` image, also `\u003chr\u003e` horizontal lines, and more.\n- [x] Support for parsing and setting styles from HTML tag attributes such as style=\"color:red\".\n- [x] Support for parsing HTML color names into UIColor/NSColor.\n- [x] Better performance compared to `NSAttributedString.DocumentType.html`.\n- [x] Fully test cases and test coverage.\n\n# Buy me a coffee ❤️❤️❤️\n\n\u003ca href=\"https://www.buymeacoffee.com/zhgchgli\" target=\"_blank\"\u003e\u003cimg width=\"545\" alt=\"bmc-button\" src=\"https://github.com/user-attachments/assets/5983bfc9-27fd-49e0-a7f4-eb07657c6e31\"\u003e\u003c/a\u003e\n\n[**If this project has helped you, feel free to sponsor me a cup of coffee, thank you.**](https://www.buymeacoffee.com/zhgchgli)\n\n## Try it!\n\n![Simulator Screen Recording - iPhone 14 Pro - 2023-03-09 at 23 38 25](https://user-images.githubusercontent.com/33706588/224075106-6a335e38-3f9c-4e1a-aee4-0414a96f2a65.gif)\n\n\nTo run the ZMarkupParser demo, download the repository and open ZMarkupParser.xcworkspace. Then, select the ZMarkupParser-Demo target and run it to start exploring the library. Enjoy!\n\n### Performance Benchmark\n\n[![Performance Benchmark](https://user-images.githubusercontent.com/33706588/221342800-d7891cb3-af1a-4fe9-a8f1-c7b963e11f95.png)](https://quickchart.io/chart-maker/view/zm-73887470-e667-4ca3-8df0-fe3563832b0b)\n\n(2022/M2/24GB Memory/macOS 13.2/XCode 14.1)\n\nNote that rendering an NSAttributedString with the DocumentType.html option can cause a crash when the length of the HTML string exceeds 54,600+ characters. To avoid this issue, consider using ZMarkupParser instead.\n\nThe chart above shows the elapsed time (in seconds) to render different HTML string lengths (x). As you can see, ZMarkupParser performs better than NSAttributedString.DocumentType.html, especially for larger HTML strings.\n\n## Installation\n\n### Swift Package Manager\n\n- File \u003e Swift Packages \u003e Add Package Dependency\n- Add `https://github.com/ZhgChgLi/ZMarkupParser.git`\n- Select \"Up to Next Major\" with \"1.12.0\"\n\nor \n\n```swift\n...\ndependencies: [\n  .package(url: \"https://github.com/ZhgChgLi/ZMarkupParser.git\", from: \"1.12.0\"),\n]\n...\n.target(\n    ...\n    dependencies: [\n        \"ZMarkupParser\",\n    ],\n    ...\n)\n```\n\n### CocoaPods\n```ruby\nsource 'https://github.com/CocoaPods/Specs.git'\nplatform :ios, '12.0'\nuse_frameworks!\n\ntarget 'MyApp' do\n  pod 'ZMarkupParser', '~\u003e 1.12.0'\nend\n```\n\n## How it works? (explain with Pseudocode)\n1. Input html string: `\u003ca\u003eLink\u003cb\u003eLinkBold\u003c/a\u003eBold\u003c/b\u003e`\n2. Convert string to array of tag element through Regex:\n```\n[\n  {tagStart: \"a\"},\n  {string: \"Link\"},\n  {tagStart: \"b\"},\n  {string: \"LinkBold\"},\n  {tagClose: \"a\"},\n  {string: \"Bold\"},\n  {tagClose: \"b\"}\n]\n```\n3. Traverse tag element array to autocorrect mixed tags and find isolated tags:\n```\n[\n  {tagStart: \"a\"},\n  {string: \"Link\"},\n  {tagStart: \"b\"},\n  {string: \"LinkBold\"},\n  {tagClose: \"b\"},\n  {tagClose: \"a\"},\n  {tagStart: \"b\"},\n  {string: \"Bold\"},\n  {tagClose: \"b\"}\n]\n```\n4. Convert tag element array to abstract syntax tree:\n```\nRootMarkup\n|--A\n|  |--String(\"Link\")\n|  |--B\n|     |--String(\"LinkBold\")\n|\n|--B\n   |--String(\"Bold\")\n```\n5. Map tag to abstract Markup/MarkupStyle:\n```\nRootMarkup\n|--A(underline=true)\n|  |--String(\"Link\")(color=blue, font=13pt)\n|  |--B\n|     |--String(\"LinkBold\")(color=blue, font=18pt, bold=true)\n|\n|--B(font=18pt, bold=true)\n```\n6. Use Visitor Pattern to visit every tree leaf Markup/MarkupStyle and combine it to NSAttributedString through recursion.\n\nResult:\n```\nLink{\n    NSColor = \"UIExtendedSRGBColorSpace 0 0.478431 1 1\";\n    NSFont = \"\u003cUICTFont: 0x145d17600\u003e font-family: \\\".SFUI-Regular\\\"; font-weight: normal; font-style: normal; font-size: 13.00pt\";\n    NSUnderline = 1;\n}LinkBold{\n    NSColor = \"UIExtendedSRGBColorSpace 0 0.478431 1 1\";\n    NSFont = \"\u003cUICTFont: 0x145d18710\u003e font-family: \\\".SFUI-Semibold\\\"; font-weight: bold; font-style: normal; font-size: 18.00pt\";\n    NSUnderline = 1;\n}Bold{\n    NSFont = \"\u003cUICTFont: 0x145d18710\u003e font-family: \\\".SFUI-Semibold\\\"; font-weight: bold; font-style: normal; font-size: 18.00pt\";\n}\n```\n\n### Example\n![ZMarkupParser Exmple](https://user-images.githubusercontent.com/33706588/220371406-d458f810-4dee-4f22-a161-b956fc626ccc.jpg)\n\n\n## Introduction\n### HTMLTagName\n\nZMarkupParser provides a set of pre-defined tag names that map to abstract markup classes, such as A_HTMLTagName() for \u003ca\u003e\u003c/a\u003e, B_HTMLTagName() for \u003cb\u003e\u003c/b\u003e, and so on. This mapping is used to create instances of the corresponding markup classes during the parsing process.\n\nIn addition, if there is a tag that is not defined or you want to customize your own tag, you can use the `ExtendTagName(tagName: String)` method to create a custom tag name and map it to an abstract markup class of your own design.\n\n```swift\nA_HTMLTagName(), // \u003ca\u003e\u003c/a\u003e\nB_HTMLTagName(), // \u003cb\u003e\u003c/b\u003e\nBR_HTMLTagName(), // \u003cbr\u003e\u003c/br\u003e and also \u003cbr/\u003e\nDIV_HTMLTagName(), // \u003cdiv\u003e\u003c/div\u003e\nHR_HTMLTagName(), // \u003chr\u003e\u003c/hr\u003e\nI_HTMLTagName(), // \u003ci\u003e\u003c/i\u003e\nLI_HTMLTagName(), // \u003cli\u003e\u003c/li\u003e\nOL_HTMLTagName(), // \u003col\u003e\u003c/ol\u003e\nP_HTMLTagName(), // \u003cp\u003e\u003c/p\u003e\nSPAN_HTMLTagName(), // \u003cspan\u003e\u003c/span\u003e\nSTRONG_HTMLTagName(), // \u003cstrong\u003e\u003c/strong\u003e\nU_HTMLTagName(), // \u003cu\u003e\u003c/u\u003e\nUL_HTMLTagName(), // \u003cul\u003e\u003c/ul\u003e\nDEL_HTMLTagName(), // \u003cdel\u003e\u003c/del\u003e\nIMG_HTMLTagName(handler: ZNSTextAttachmentHandler), // \u003cimg\u003e and image downloader\nTR_HTMLTagName(), // \u003ctr\u003e\nTD_HTMLTagName(), // \u003ctd\u003e\nTH_HTMLTagName(), // \u003cth\u003e\n...and more\n```\n\n### MarkupStyle/MarkupStyleColor/MarkupStyleParagraphStyle\nThe MarkupStyle wrapper contains various properties that are used to define the attributes of an NSAttributedString. These properties includes:\n```swift\nvar font:MarkupStyleFont\nvar paragraphStyle:MarkupStyleParagraphStyle\nvar foregroundColor:MarkupStyleColor? = nil\nvar backgroundColor:MarkupStyleColor? = nil\nvar ligature:NSNumber? = nil\nvar kern:NSNumber? = nil\nvar tracking:NSNumber? = nil\nvar strikethroughStyle:NSUnderlineStyle? = nil\nvar underlineStyle:NSUnderlineStyle? = nil\nvar strokeColor:MarkupStyleColor? = nil\nvar strokeWidth:NSNumber? = nil\nvar shadow:NSShadow? = nil\nvar textEffect:String? = nil\nvar attachment:NSTextAttachment? = nil\nvar link:URL? = nil\nvar baselineOffset:NSNumber? = nil\nvar underlineColor:MarkupStyleColor? = nil\nvar strikethroughColor:MarkupStyleColor? = nil\nvar obliqueness:NSNumber? = nil\nvar expansion:NSNumber? = nil\nvar writingDirection:NSNumber? = nil\nvar verticalGlyphForm:NSNumber? = nil\n...\n```\n\nFor example, you can initialize or define a MarkupStyle object with the properties you want, such as setting the font size to 13 and the background color to aquamarine:\n```swift\nMarkupStyle(font: MarkupStyleFont(size: 13), backgroundColor: MarkupStyleColor(name: .aquamarine))\n```\n\n### HTMLTagStyleAttribute\n\nThese are pre-defined style attributes that can be used in the conversion of HTML tags to NSAttributedString attributes. Each style attribute has a corresponding class that defines its behavior and how it should be applied to the NSAttributedString.\n\n```swift\nColorHTMLTagStyleAttribute(), // color\nBackgroundColorHTMLTagStyleAttribute(), // background-color\nFontSizeHTMLTagStyleAttribute(), // font-size\nFontWeightHTMLTagStyleAttribute(), // font-weight\nLineHeightHTMLTagStyleAttribute(), // line-height\nWordSpacingHTMLTagStyleAttribute(), // word-spacing\n```\n\nIf there is a style attribute that is not defined, the ExtendHTMLTagStyleAttribute class can be used to define it. This class takes in a style name and a closure that takes in an existing style and the value of the new style attribute and returns a new style with the new attribute applied.\n\nFor exmaple: `style=\"text-decoration\"`\n```swift\nExtendHTMLTagStyleAttribute(styleName: \"text-decoration\", render: { fromStyle, value in\n  var newStyle = fromStyle\n  if value == \"underline\" {\n    newStyle.underline = NSUnderlineStyle.single\n  } else {\n    // ...  \n  }\n  return newStyle\n})\n```\n\n## Usage\n```swift\nimport ZMarkupParser\n```\n\n### Builder Pattern to Build Parser\n```swift\nlet parser = ZHTMLParserBuilder.initWithDefault().set(rootStyle: MarkupStyle(font: MarkupStyleFont(size: 13)).build()\n```\n\nThe code initializes a new ZHTMLParserBuilder object with default settings using the `initWithDefault()` method. This method adds all pre-defined HTML tag names and style attributes, and sets the tag's default MarkupStyle to render.\n\nThen, the `set(rootStyle: MarkupStyle)` method is called to specify the default root style to render. This root style will be applied to the entire attributed string that is generated by the parser.\n\nFinally, the `build()` method is called at the end to generate the parser object.\n\n\n#### Customized List Item Tag (`ul/ol`)\n```\nlet parser = ZHTMLParserBuilder.initWithDefault().add(OL_HTMLTagName(), withCustomStyle: MarkupStyle(paragraphStyle: MarkupStyleParagraphStyle(textListStyleType: .circle, textListHeadIndent: 4, textListIndent: 8))).build()\n```\n![ZhgChgLi-2024-05-30_23-25-57](https://github.com/ZhgChgLi/ZMarkupParser/assets/33706588/6f4fe41d-4c22-4e3a-a6cd-967902a7d99d)\n\n#### Customized Tag Style/Extend Tag Name\n\nThese code snippets demonstrate how to customize the style of a tag or extend the tag name:\n\nTo customize the style of a tag, you can use the add method of the ZHTMLParserBuilder class and provide an instance of HTMLTagName and a MarkupStyle object as parameters. For example, the following code snippet will use a custom markup style to render the \u003cb\u003e\u003c/b\u003e tag:\n```swift\nlet parser = ZHTMLParserBuilder.initWithDefault().add(B_HTMLTagName(), withCustomStyle: MarkupStyle(font: MarkupStyleFont(size: 18, weight: .style(.semibold)))).build()\n```\n\nTo extend the tag name and customize its style, you can use the ExtendTagName class and the add method of the ZHTMLParserBuilder class. For example, the following code snippet will extend the tag name to \u003czhgchgli\u003e\u003c/zhgchgli\u003e and use a custom markup style to render it:\n```swift\nlet parser = ZHTMLParserBuilder.initWithDefault().add(ExtendTagName(\"zhgchgli\"), withCustomStyle: MarkupStyle(backgroundColor: MarkupStyleColor(name: .aquamarine))).build()\n```\n\n#### Support for Class/ID Style Mapping and Parsing\n\nThe class HTML attribute can use the HTMLTagClassAttribute to define classNames with pre-defined styles.\n\nHTML allows specifying multiple `class` attributes separated by spaces, but the `id` attribute can only be assigned a single value per HTML tag.\n\ne.g.:\n```\n\u003cspan id=\"header\"\u003ehey\u003c/span\u003ehey \u003cspan id=\"text-red text-small\"\u003eTeste de texto text small\u003c/span\u003e hey\u003cspan class=\"text-red\"\u003ehey\u003c/span\u003eheyhey\n```\n\n```\nlet parser = ZHTMLParserBuilder.initWithDefault().add(HTMLTagClassAttribute(className: \"text-red\", render: {\n    return MarkupStyle(foregroundColor: MarkupStyleColor(color: .red))\n})).add(HTMLTagClassAttribute(className: \"text-small\", render: {\n    return MarkupStyle(font: MarkupStyleFont(.systemFont(ofSize: 6)))\n})).add(HTMLTagIdAttribute(idName: \"header\", render: {\n    return MarkupStyle(font: MarkupStyleFont(.systemFont(ofSize: 36)))\n})).build()\n```\n\n### Render HTML String\n```swift\nparser.render(htmlString) // NSAttributedString\n\nBy default, ZMarkupParser will decode HTML entities using the [HTMLString](https://github.com/alexisakers/HTMLString) library.\n\nIf you wish to keep the raw data clean, you can disable this feature by using the following syntax: `parser.render(htmlString, forceDecodeHTMLEntities: false)` or `setHtmlString(attributedString, with: parser, forceDecodeHTMLEntities: false)`.\n\n// work with UITextView\ntextView.setHtmlString(htmlString)\n\n// work with UILabel\nlabel.setHtmlString(htmlString)\n```\n\n### Stripper HTML String\n```swift\nparser.stripper(htmlString) // NSAttributedString\n```\n\n### Selector HTML String\n```swift\nlet selector = parser.selector(htmlString) // HTMLSelector e.g. input: \u003ca\u003e\u003cb\u003eTest\u003c/b\u003eLink\u003c/a\u003e\nselector.first(\"a\")?.first(\"b\").attributedString // will return Test\nselector.filter(\"a\").get() // will return dict struct\nselector.filter(\"a\") // will return json string of dict\n\n```\n\n### Selector+Render HTML String\n```swift\nlet selector = parser.selector(htmlString) // HTMLSelector e.g. input: \u003ca\u003e\u003cb\u003eTest\u003c/b\u003eLink\u003c/a\u003e\nparser.render(selector.first(\"a\")?.first(\"b\"))\n```\n\n### With Async\n```swift\nparser.render(String) { _ in }...\nparser.stripper(String) { _ in }...\nparser.selector(String) { _ in }...\n```\nIf you want to render huge html string, please use async instead.\n\n## Things to know\n- To change the style of links in UITextView, you need to set the linkTextAttributes property to an NSAttributedString.Key value that includes the desired style properties.\n- If you're using a UILabel to render attributed strings, note that you can't change the color of .link text using the NSAttributedString.Key.foregroundColor attribute.\n- The ZHTMLParser library is intended for rendering partial HTML content, and may not be suitable for rendering very large or complex HTML documents. For these use cases, it's better to use a web view to render the HTML content.\n\n## Sponsor\n\n- If this project has been helpful to you, I would greatly appreciate your support.\n- If you're willing, you can consider buying me a cup of coffee as a token of encouragement.\n- Your brand will be displayed here, and the colors will be implemented in [MarkupStyleSponsorColor](https://github.com/ZhgChgLi/ZMarkupParser/blob/main/Sources/ZMarkupParser/Core/MarkupStyle/MarkupStyleSponsorColor.swift).\n- You can initialize the color directly using `MarkupStyleColor(sponsor: .pinkoi(.navy))`.\n\n## Who is using\n[![pinkoi](https://user-images.githubusercontent.com/33706588/221343295-3e3831e6-f76d-430a-87e3-4daf9815297d.jpg)](https://en.pinkoi.com)\n\n[Pinkoi.com](https://en.pinkoi.com) is Asia's leading online marketplace for original design goods, digital creations, and workshop experiences.\n\n---\n- If your project/product is using ZMarkupParser, feel free to create a PR (Pull Request) here (the \"Who is using\" section in the Readme file). :)\n- Please also add your brand and brand colors to [MarkupStyleVendorColor.swift](https://github.com/ZhgChgLi/ZMarkupParser/blob/main/Sources/ZMarkupParser/Core/MarkupStyle/MarkupStyleVendorColor.swift).\n- You can initialize the color directly using `MarkupStyleVendorColor(vendor: .pinkoi(.navy))`.\n\n## About\n- [ZhgChg.Li](https://zhgchg.li/)\n- [ZhgChgLi's Medium](https://blog.zhgchg.li/)\n\n## Other works\n### Swift Libraries\n- [ZMarkupParser](https://github.com/ZhgChgLi/ZMarkupParser) is a pure-Swift library that helps you to convert HTML strings to NSAttributedString with customized style and tags.\n- [ZPlayerCacher](https://github.com/ZhgChgLi/ZPlayerCacher) is a lightweight implementation of the AVAssetResourceLoaderDelegate protocol that enables AVPlayerItem to support caching streaming files.\n\n### Integration Tools\n- [XCFolder](https://github.com/ZhgChgLi/XCFolder) is a powerful command-line tool that converts Xcode virtual groups into actual directories, reorganizing your project structure to align with Xcode groups and enabling seamless integration with modern Xcode project generation tools like Tuist and XcodeGen.\n- [ZReviewTender](https://github.com/ZhgChgLi/ZReviewTender) is a tool for fetching app reviews from the App Store and Google Play Console and integrating them into your workflow.\n- [ZMediumToMarkdown](https://github.com/ZhgChgLi/ZMediumToMarkdown) is a powerful tool that allows you to effortlessly download and convert your Medium posts to Markdown format.\n- [linkyee](https://github.com/ZhgChgLi/linkyee) is a fully customized, open-source LinkTree alternative deployed directly on GitHub Pages.\n\n\n# Donate\n\n[![Buy Me A Coffe](https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20beer!\u0026emoji=%F0%9F%8D%BA\u0026slug=zhgchgli\u0026button_colour=FFDD00\u0026font_colour=000000\u0026font_family=Bree\u0026outline_colour=000000\u0026coffee_colour=ffffff)](https://www.buymeacoffee.com/zhgchgli)\n\nIf you find this library helpful, please consider starring the repo or recommending it to your friends.\n\nFeel free to open an issue or submit a fix/contribution via pull request. :)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FZhgChgLi%2FZMarkupParser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FZhgChgLi%2FZMarkupParser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FZhgChgLi%2FZMarkupParser/lists"}