Markdown parsing and rendering for iOS and OS X

Related tags

Text CocoaMarkdown
Overview

CocoaMarkdown

Markdown parsing and rendering for iOS and macOS

CocoaMarkdown is a cross-platform framework for parsing and rendering Markdown, built on top of the C reference implementation of CommonMark.

Why?

CocoaMarkdown aims to solve two primary problems better than existing libraries:

  1. More flexibility. CocoaMarkdown allows you to define custom parsing hooks or even traverse the Markdown AST using the low-level API.
  2. Efficient NSAttributedString creation for easy rendering on iOS and macOS. Most existing libraries just generate HTML from the Markdown, which is not a convenient representation to work with in native apps.

Example app macOS

Example app iOS

Installation

First you will want to add this project as a submodule to your project:

git submodule add https://github.com/indragiek/CocoaMarkdown.git

Then, you need to pull down all of its dependencies.

cd CocoaMarkdown
git submodule update --init --recursive

Next, drag the .xcodeproj file from within CocoaMarkdown into your project. After that, click on the General tab of your target. Select the plus button under "Embedded Binaries" and select the CocoaMarkdown.framework.

API

Traversing the Markdown AST

CMNode and CMIterator wrap CommonMark's C types with an object-oriented interface for traversal of the Markdown AST.

let document = CMDocument(contentsOfFile: path, options: [])
document.rootNode.iterator().enumerateUsingBlock { (node, _, _) in
    print("String value: \(node.stringValue)")
}

Building Custom Renderers

The CMParser class isn't really a parser (it just traverses the AST), but it defines an NSXMLParser-style delegate API that provides handy callbacks for building your own renderers:

@protocol CMParserDelegate <NSObject>
@optional
- (void)parserDidStartDocument:(CMParser *)parser;
- (void)parserDidEndDocument:(CMParser *)parser;
...
- (void)parser:(CMParser *)parser foundText:(NSString *)text;
- (void)parserFoundHRule:(CMParser *)parser;
...
@end

CMAttributedStringRenderer is an example of a custom renderer that is built using this API.

Rendering Attributed Strings

CMAttributedStringRenderer is the high level API that will be useful to most apps. It creates an NSAttributedString directly from Markdown, skipping the step of converting it to HTML altogether.

Going from a Markdown document to rendering it on screen is as easy as:

let document = CMDocument(contentsOfFile: path, options: [])
let renderer = CMAttributedStringRenderer(document: document, attributes: CMTextAttributes())
textView.attributedText = renderer.render()

Or, using the convenience method on CMDocument:

textView.attributedText = CMDocument(contentsOfFile: path, options: []).attributedStringWithAttributes(CMTextAttributes())

HTML elements can be supported by implementing CMHTMLElementTransformer. The framework includes several transformers for commonly used tags:

Transformers can be registered with the renderer to use them:

let document = CMDocument(contentsOfFile: path, options: [])
let renderer = CMAttributedStringRenderer(document: document, attributes: CMTextAttributes())
renderer.registerHTMLElementTransformer(CMHTMLStrikethroughTransformer())
renderer.registerHTMLElementTransformer(CMHTMLSuperscriptTransformer())
textView.attributedText = renderer.render()

Customizing Attributed strings rendering

All attributes used to style the text are customizable using the CMTextAttributes class.

Every Markdown element type can be customized using the corresponding CMStyleAttributes property in CMTextAttributes, defining 3 different kinds of attributes:

  • String attributes, i.e. regular NSAttributedString attributes
  • Font attributes, for easy font setting
  • Paragraph attributes, relevant only for block elements

Attributes for any Markdown element kind can be directly set:

let textAttributes = CMTextAttributes()
textAttributes.linkAttributes.stringAttributes[NSAttributedString.Key.backgroundColor] = UIColor.yellow

A probably better alternative for style customization is to use grouped attributes setting methods available in CMTextAttributes:

let textAttributes = CMTextAttributes()

// Set the text color for all headers
textAttributes.addStringAttributes([ .foregroundColor: UIColor(red: 0.0, green: 0.446, blue: 0.657, alpha: 1.0)], 
                                   forElementWithKinds: .anyHeader)

// Set a specific font + font-traits for all headers
let boldItalicTrait: UIFontDescriptor.SymbolicTraits = [.traitBold, .traitItalic]
textAttributes.addFontAttributes([ .family: "Avenir Next" ,
                                   .traits: [ UIFontDescriptor.TraitKey.symbolic: boldItalicTrait.rawValue]], 
                                 forElementWithKinds: .anyHeader)
// Set specific font traits for header1 and header2
textAttributes.setFontTraits([.weight: UIFont.Weight.heavy], 
                             forElementWithKinds: [.header1, .header2])

// Center block-quote paragraphs        
textAttributes.addParagraphStyleAttributes([ .alignment: NSTextAlignment.center.rawValue], 
                                           forElementWithKinds: .blockQuote)

// Set a background color for code elements        
textAttributes.addStringAttributes([ .backgroundColor: UIColor(white: 0.9, alpha: 0.5)], 
                                   forElementWithKinds: [.inlineCode, .codeBlock])

List styles can be customized using dedicated paragraph style attributes:

// Customize the list bullets
textAttributes.addParagraphStyleAttributes([ .listItemBulletString: "๐Ÿ" ], 
                                           forElementWithKinds: .unorderedList)
textAttributes.addParagraphStyleAttributes([ .listItemBulletString: "๐ŸŒผ" ], 
                                           forElementWithKinds: .unorderedSublist)

// Customize numbered list item labels format and distance between label and paragraph
textAttributes.addParagraphStyleAttributes([ .listItemNumberFormat: "(%02ld)", 
                                             .listItemLabelIndent: 30 ],    
                                           forElementWithKinds: .orderedList)

Font and paragraph attributes are incremental, meaning that they allow to modify only specific aspects of the default rendering styles.

Additionally on iOS, Markdown elements styled using the font attributes API get automatic Dynamic-Type compliance in the generated attributed string, just like default rendering styles.

Rendering HTML

CMHTMLRenderer provides the ability to render HTML from Markdown:

let document = CMDocument(contentsOfFile: path, options: [])
let renderer = CMHTMLRenderer(document: document)
let HTML = renderer.render()

Or, using the convenience method on CMDocument:

let HTML = CMDocument(contentsOfFile: path).HTMLString()

Example Apps

The project includes example apps for iOS and macOS to demonstrate rendering attributed strings.

Contact

License

CocoaMarkdown is licensed under the MIT License. See LICENSE for more information.

Issues
  • Fix flipped images on macOS 10.15

    Fix flipped images on macOS 10.15

    MacOS Catalina fixes a flipping issue regarding the display of image attachments in attributed strings. Therefore the workaround included in CMImageTextAttachment.m is could cause markdown images to appear vertically flipped on macOS 10.15.

    So this pull request conditionally removes the extra image flipping when the masOS version is 10.15 or above.

    It also includes a new CMDocument init method added in a previous commit.

    opened by jlj 10
  • Image Support

    Image Support

    I noticed that the images are not being implemented in the CMAttributedStringRenderer. Is there a plan for that?

    I've been trying to work on an asynchronous version and getting some progress here.

    opened by jamztang 10
  • Links are white

    Links are white

    let attributes = CMTextAttributes() attributes.linkAttributes = [ NSForegroundColorAttributeName: UIColor.redColor() ]

    Will render white links if the window.tintColor property is set...

    Aside from that the code above actually doesn't work at all. Starting to think the external tints colors are causing this issue?

    Any solid explanation why?

    opened by adamdahan 7
  • Add podspec

    Add podspec

    Very minimal support for iOS only. I bet OS X should work out of box if we add :osx to pod spec but I didn't test. The source code is left untouched. I removed CocoaMarkdown/Ono.h from CocoaMarkdown.h because it's not used anywhere but in CMAttributedStringRenderer.m where it is included as Ono.h which should work just fine for the time being.

    This work is based on @krodak's effort in #19 but takes into account the fact that you would like to keep submodules.

    Good to have in future:

    1. Separate private headers from public. Currently pod spec includes the entire folder but I bet there were some private headers that normally should not be exposed.
    2. Normalize import statements for dependencies, so they follow <LIBRARY/LIBRARY.h> pattern or something like that to avoid collisions.

    After merge:

    You would have to change the git URL in podspec, add tag to repo and put this tag in pod spec.

    opened by pronebird 6
  • Corrected fix for flipped images on macOS 10.15.

    Corrected fix for flipped images on macOS 10.15.

    Improved the original fix for flipped images in [b2397d6]: The SDK version used for generating the application shall be considered as well as the OS version on which the application is running (otherwise an application compiled with Xcode 10 and running on macOS 10,15 would show flipped images).

    Thanks to @hisaac for reporting the issue! (See the discussion at https://github.com/indragiek/CocoaMarkdown/pull/52#issuecomment-557681225)

    Also, to avoid creating a dedicated PR, this one includes a few minor typo fixes in the README :)

    opened by jlj 5
  • cocoapods

    cocoapods

    pod search CocoaMarkdown [!] Unable to find a pod with name, author, summary, or description matching CocoaMarkdown

    opened by 0x1306a94 4
  • Better code block

    Better code block

    Is there plans for syntax highlighting, and even potential inline scroll instead of word wrapping?

    opened by jamztang 3
  • Image renders as a placeholder until user clicks on it

    Image renders as a placeholder until user clicks on it

    Hi, I'm using the inline image functionality which was recently merged into the master branch. However, when I specify an image in markdown, I get a grey "question mark" in a box, which I see the code draws as a placeholder image. But the real image never appears until I click on that question mark, and then it instantly shows up.

    Any advice? I am not fluent in NSAttributedStrings or NSTextAttachemts unfortunately.

    opened by tsfischer 3
  • build error

    build error

    how to build ? cmark is empty!!!

    opened by szl-926929 3
  • cmark has been deprecated

    cmark has been deprecated

    For some reason, the pod is deprecated.

    opened by pronebird 0
  • Custom hooks?

    Custom hooks?

    I am using CocoaMarkdown for my open source macOS reddit client, and I was wondering if there is a way to create a custom hook? for example, if I want cocoaMarkdown to catch /r/subreddit text and highlight it and make it a clickable link. Is that possible using this library?

    opened by JoeyBodnar 1
  • Use pod to directly reference the master branch code, running crash

    Use pod to directly reference the master branch code, running crash

    image Use pod to directly reference the master branch code, running crash image But there is no problem with the way of drag the .xcodeproj file. What is the difference between the two? @indragiek @jlj

    opened by SarielTang 1
  • Supporting URLRequest in inline image download

    Supporting URLRequest in inline image download

    Hi, is there any way to add URLRequest in the inline image download method? @indragiek

    opened by rizwan95 11
  • cache image

    cache image

    Did the author consider caching the image locally

    opened by mohuilin 0
  • Link by cocopods with duplicate symbol error

    Link by cocopods with duplicate symbol error

    • Pod version : 1.8.4
    • Xcode 11.2
    • iPhone device 12.4.2
    Podfile:
    pod "CocoaMarkdown", :git => 'https://github.com/indragiek/CocoaMarkdown.git'
    

    Link error:

    duplicate symbol '_main' in:
        /Users/chenyn/Library/Developer/Xcode/DerivedData/CaJian-gdqwwrpegxglvygdepdetmfjtucl/Build/Intermediates.noindex/CaJian.build/Debug-iphoneos/CaJian.build/Objects-normal/arm64/main.o
        /Users/chenyn/Library/Developer/Xcode/DerivedData/CaJian-gdqwwrpegxglvygdepdetmfjtucl/Build/Products/Debug-iphoneos/cmark/libcmark.a(main.o)
    ld: 1 duplicate symbol for architecture arm64
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    

    Is this an error related to my environment or resulted from any incorrect operation?

    opened by lgb1234a 0
  • Failed to generate the attributed string for given text "">

    Failed to generate the attributed string for given text ""

    Below is the code to generate an AttributedString in iOS

    NSString * markdown = @"" CMDocument *doc = [[CMDocument alloc] initWithData: [markdown dataUsingEncoding: NSUTF8StringEncoding] options: 0]; CMTextAttributes *markdownAttributes = [[CMTextAttributes alloc]init]; CMAttributedStringRenderer *renderer = [[CMAttributedStringRenderer alloc] initWithDocument: doc attributes: markdownAttributes]; return [render render];

    opened by srinivas-kurakula 0
  • Probably code block bug with trailing ```

    Probably code block bug with trailing ```

    what I did

    NSString *md = @"```c\n//This is a code block\n```";
    NSData *data = [md dataUsingEncoding:NSUTF8StringEncoding];
    CMDocument *document = [[CMDocument alloc] initWithData:data options:0];
    CMAttributedStringRenderer *renderer = [[CMAttributedStringRenderer alloc] initWithDocument:document attributes:[CMTextAttributes new]];
    NSAttributedString *attributedString = [renderer render];
    NSLog(@"%@", attributedString);
    

    What I expect

    //This is a code block{
        NSFont = ...
    }
    

    What I got

    //This is a code block
    ```{
        NSFont = ...
    }
    

    Note that the trailing ``` is included in the returned NSAttributedString

    opened by hkalexling 0
  • Superscript Support in Markdown

    Superscript Support in Markdown

    Heya! Thanks for the wonderful parser, it's by far the best one I've used yet.

    The only markdown elements which seem to be unparsable using CocoaMarkdown (besides tables, which would be pretty impossible to render without ASCII art anyway) are superscript.

    Although I see that superscript HTML characters can be transformed into attributes using the CMHTMLSuperscriptTransformer class, there is no equivalent convenience for markdown. In fact, after running through the codebase for a while, it only makes sense to modify cmark itself to add this parsing ability... it's not a part of the delegate pattern or customization of CocoaMarkdown to add different markdown parsing tokens. This makes sense, but means that adding my necessary features to the markdown capability of CocoaMarkdown requires modifying an underlying library/framework (which ideally would be separable/detachable and not dependent; because CocoaMarkdown is already an extension on cmark, making an extension on the cmark that CocoaMarkdown uses would be inappropriate to upkeep (a fork inside a fork?!)).

    Instead of diving that deep and modifying the C/C++ base itself, I wanted to create this issue so we could perhaps find a solution that involves CocoaMarkdown itself and extends the modular nature of the library (so perhaps other tokens can be added in the future, too). Although the delegate pattern is very convenient and useful, it does not seem appropriate for modifying parsing, only the attributes/effects of the parse-in-progress. Hope this is a reasonable request, cheers!

    opened by insanj 2
Owner
Indragie Karunaratne
Indragie Karunaratne
Markdown syntax highlighter for iOS

Marklight Markdown syntax highlighter for iOS and macOS. Description Marklight is a drop in component to easily add realtime Markdown syntax highlight

Matteo Gavagnin 526 Nov 24, 2021
Rich Markdown editing control for iOS

MarkdownTextView Rich Markdown Editing for iOS MarkdownTextView is an iOS framework for adding rich Markdown editing capabilities. Support for Markdow

Indragie Karunaratne 671 Dec 2, 2021
[iOS] A fully themeable markdown editor with live syntax highlighting.

Usage let notepad = Notepad(frame: view.bounds, themeFile: "one-dark") view.addSubview(notepad) Notepad is just like any other UITextView, but you nee

Rudd Fawcett 740 Jan 4, 2022
Markdown parser for iOS

Marky Mark Marky Mark is a parser written in Swift that converts markdown into native views. The way it looks it highly customizable and the supported

M2mobi 266 Jan 1, 2022
Markdown View for iOS.

MarkdownView MarkdownView is a WKWebView based UI element, and internally use bootstrap, highlight.js, markdown-it. How to use import MarkdownView le

Keita Oouchi 1.7k Jan 14, 2022
Markdown parser for iOS

Marky Mark Marky Mark is a parser written in Swift that converts markdown into native views. The way it looks it highly customizable and the supported

M2mobi 249 Apr 13, 2021
Converts Markdown files and strings into NSAttributedStrings with lots of customisation options.

SwiftyMarkdown 1.0 SwiftyMarkdown converts Markdown files and strings into NSAttributedStrings using sensible defaults and a Swift-style syntax. It us

Simon Fairbairn 1.4k Jan 2, 2022
A simple and customizable Markdown Parser for Swift

MarkdownKit MarkdownKit is a customizable and extensible Markdown parser for iOS and macOS. It supports many of the standard Markdown elements through

Bruno Oliveira 630 Dec 29, 2021
An Objective-C framework for converting Markdown to HTML.

MMMarkdown MMMarkdown is an Objective-C framework for converting Markdown to HTML. It is compatible with OS X 10.7+, iOS 8.0+, tvOS, and watchOS. Unli

Matt Diephouse 1.2k Jan 6, 2022
A Pure Swift implementation of the markdown mark-up language

SmarkDown A pure Swift markdown implementation consistent with Gruber's 1.0.1 version. It is released under the BSD license so please feel free to use

Swift Studies 68 Dec 30, 2021
A Swift framework for parsing, formatting and validating international phone numbers. Inspired by Google's libphonenumber.

PhoneNumberKit Swift 5.3 framework for parsing, formatting and validating international phone numbers. Inspired by Google's libphonenumber. Features F

Roy Marmelstein 4.4k Jan 9, 2022
Swift emoji string parsing library

Croc is a library for parsing emojis on iOS. It provides a simple and lightweight interface for detecting, generating, categorizing and managing emoji

Joe Kalash 125 Dec 23, 2021
Swift emoji string parsing library

Croc is a library for parsing emojis on iOS. It provides a simple and lightweight interface for detecting, generating, categorizing and managing emoji

Joe Kalash 124 Mar 18, 2021
Powerful text framework for iOS to display and edit rich text.

YYText Powerful text framework for iOS to display and edit rich text. (It's a component of YYKit) Features UILabel and UITextView API compatible High

null 8.7k Jan 7, 2022
String (and more) validation for iOS

Swift Validators ?? String validation for iOS. Contents Installation Walkthrough Usage Available validators License ReactiveSwift + SwiftValidators Wa

George Kaimakas 234 Dec 9, 2021
Swift String Validator. Simple lib for ios to validate string and UITextFields text for some criterias

Swift String validator About Library for easy and fastest string validation based on ัciterias. Instalation KKStringValidator is available through Coc

Kostya 17 Dec 20, 2019
๐ŸŒโฉ๐Ÿ“„ Convert ISO8859 1-16 Encoded Text to String in Swift. Supports iOS, tvOS, watchOS and macOS.

ISO8859 Convert ISO8859 1-16 Encoded Text to String in Swift. Usage let encoding = ISO8859.part1 let string = String([...], iso8859Encoding: encoding)

Devran Cosmo Uenal 17 Nov 12, 2020
A library for formatting strings on iOS and macOS

Sprinter Introduction What? Why? How? Usage Installation Integration Localization Thread Safety Advanced Usage Introduction What? Sprinter is a librar

Nick Lockwood 166 Nov 8, 2021
CodeMirror-Swift is a lightweight wrapper of CodeMirror for macOS and iOS

CodeMirror-Swift is a lightweight wrapper of CodeMirror for macOS and iOS. Features ?? Lightweight CodeMirror wrapper (build 5.52.2) โœ… 100% Native Swi

Proxyman 64 Dec 13, 2021