Browse Source

Initial commit

xcbosa mbp16 1 year ago
commit
aab35ebc58
87 changed files with 8597 additions and 0 deletions
  1. BIN
      .DS_Store
  2. 18 0
      .gitignore
  3. 12 0
      .gitmodules
  4. 27 0
      CocoaMarkdown.podspec
  5. 1925 0
      CocoaMarkdown.xcodeproj/project.pbxproj
  6. 7 0
      CocoaMarkdown.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  7. 37 0
      CocoaMarkdown.xcodeproj/project.xcworkspace/xcshareddata/CocoaMarkdown.xcscmblueprint
  8. 113 0
      CocoaMarkdown.xcodeproj/xcshareddata/xcschemes/CocoaMarkdown-Mac.xcscheme
  9. 113 0
      CocoaMarkdown.xcodeproj/xcshareddata/xcschemes/CocoaMarkdown-iOS.xcscheme
  10. 91 0
      CocoaMarkdown.xcodeproj/xcshareddata/xcschemes/Example-Mac.xcscheme
  11. 91 0
      CocoaMarkdown.xcodeproj/xcshareddata/xcschemes/Example-iOS.xcscheme
  12. 25 0
      CocoaMarkdown/CMAttributeRun.h
  13. 33 0
      CocoaMarkdown/CMAttributeRun.m
  14. 47 0
      CocoaMarkdown/CMAttributedStringRenderer.h
  15. 475 0
      CocoaMarkdown/CMAttributedStringRenderer.m
  16. 24 0
      CocoaMarkdown/CMCascadingAttributeStack.h
  17. 319 0
      CocoaMarkdown/CMCascadingAttributeStack.m
  18. 25 0
      CocoaMarkdown/CMDocument+AttributedStringAdditions.h
  19. 20 0
      CocoaMarkdown/CMDocument+AttributedStringAdditions.m
  20. 21 0
      CocoaMarkdown/CMDocument+HTMLAdditions.h
  21. 19 0
      CocoaMarkdown/CMDocument+HTMLAdditions.m
  22. 92 0
      CocoaMarkdown/CMDocument.h
  23. 74 0
      CocoaMarkdown/CMDocument.m
  24. 16 0
      CocoaMarkdown/CMDocument_Private.h
  25. 20 0
      CocoaMarkdown/CMHTMLElement.h
  26. 28 0
      CocoaMarkdown/CMHTMLElement.m
  27. 39 0
      CocoaMarkdown/CMHTMLElementTransformer.h
  28. 36 0
      CocoaMarkdown/CMHTMLRenderer.h
  29. 31 0
      CocoaMarkdown/CMHTMLRenderer.m
  30. 13 0
      CocoaMarkdown/CMHTMLScriptTransformer.h
  31. 67 0
      CocoaMarkdown/CMHTMLScriptTransformer.m
  32. 24 0
      CocoaMarkdown/CMHTMLScriptTransformer_Private.h
  33. 37 0
      CocoaMarkdown/CMHTMLStrikethroughTransformer.h
  34. 50 0
      CocoaMarkdown/CMHTMLStrikethroughTransformer.m
  35. 51 0
      CocoaMarkdown/CMHTMLSubscriptTransformer.h
  36. 33 0
      CocoaMarkdown/CMHTMLSubscriptTransformer.m
  37. 51 0
      CocoaMarkdown/CMHTMLSuperscriptTransformer.h
  38. 33 0
      CocoaMarkdown/CMHTMLSuperscriptTransformer.m
  39. 37 0
      CocoaMarkdown/CMHTMLUnderlineTransformer.h
  40. 50 0
      CocoaMarkdown/CMHTMLUnderlineTransformer.m
  41. 14 0
      CocoaMarkdown/CMHTMLUtilities.h
  42. 61 0
      CocoaMarkdown/CMHTMLUtilities.m
  43. 26 0
      CocoaMarkdown/CMImageTextAttachment.h
  44. 237 0
      CocoaMarkdown/CMImageTextAttachment.m
  45. 63 0
      CocoaMarkdown/CMIterator.h
  46. 67 0
      CocoaMarkdown/CMIterator.m
  47. 181 0
      CocoaMarkdown/CMNode.h
  48. 239 0
      CocoaMarkdown/CMNode.m
  49. 25 0
      CocoaMarkdown/CMNode_Private.h
  50. 116 0
      CocoaMarkdown/CMParser.h
  51. 292 0
      CocoaMarkdown/CMParser.m
  52. 44 0
      CocoaMarkdown/CMPlatformDefines.h
  53. 20 0
      CocoaMarkdown/CMStack.h
  54. 41 0
      CocoaMarkdown/CMStack.m
  55. 296 0
      CocoaMarkdown/CMTextAttributes.h
  56. 458 0
      CocoaMarkdown/CMTextAttributes.m
  57. 29 0
      CocoaMarkdown/CocoaMarkdown.h
  58. 41 0
      CocoaMarkdown/Configuration/cmark_export.h
  59. 7 0
      CocoaMarkdown/Configuration/cmark_version.h
  60. 23 0
      CocoaMarkdown/Configuration/config.h
  61. 28 0
      CocoaMarkdown/Info.plist
  62. 33 0
      CocoaMarkdownTests/CMDocumentSpec.m
  63. 15 0
      CocoaMarkdownTests/CMHTMLRendererSpec.m
  64. 49 0
      CocoaMarkdownTests/CMIteratorSpec.m
  65. 85 0
      CocoaMarkdownTests/CMNodeSpec.m
  66. 85 0
      CocoaMarkdownTests/CMParserSpec.m
  67. 50 0
      CocoaMarkdownTests/CMParserTestObject.h
  68. 231 0
      CocoaMarkdownTests/CMParserTestObject.m
  69. 4 0
      CocoaMarkdownTests/CocoaMarkdownTests-Bridging-Header.h
  70. 10 0
      CocoaMarkdownTests/DummySpec.swift
  71. 24 0
      CocoaMarkdownTests/Info.plist
  72. 45 0
      CocoaMarkdownTests/Resources/test.md
  73. 89 0
      Example-Mac/AppDelegate.swift
  74. 779 0
      Example-Mac/Base.lproj/MainMenu.xib
  75. 58 0
      Example-Mac/Images.xcassets/AppIcon.appiconset/Contents.json
  76. 6 0
      Example-Mac/Images.xcassets/Contents.json
  77. 34 0
      Example-Mac/Info.plist
  78. 47 0
      Example-iOS/AppDelegate.swift
  79. 41 0
      Example-iOS/Base.lproj/LaunchScreen.xib
  80. 47 0
      Example-iOS/Base.lproj/Main.storyboard
  81. 38 0
      Example-iOS/Images.xcassets/AppIcon.appiconset/Contents.json
  82. 40 0
      Example-iOS/Info.plist
  83. 45 0
      Example-iOS/ViewController.swift
  84. 19 0
      LICENSE
  85. 191 0
      README.md
  86. BIN
      images/example-app-iOS.png
  87. BIN
      images/example-app-mac.png

BIN
.DS_Store


+ 18 - 0
.gitignore

@@ -0,0 +1,18 @@
+# Xcode
+#
+build/
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata
+*.xccheckout
+*.moved-aside
+DerivedData
+*.hmap
+*.ipa
+*.xcuserstate

+ 12 - 0
.gitmodules

@@ -0,0 +1,12 @@
+[submodule "External/Quick"]
+	path = External/Quick
+	url = https://github.com/Quick/Quick
+[submodule "External/Nimble"]
+	path = External/Nimble
+	url = https://github.com/Quick/Nimble.git
+[submodule "External/Ono"]
+	path = External/Ono
+	url = https://github.com/mattt/Ono.git
+[submodule "External/cmark"]
+	path = External/cmark
+	url = https://github.com/jgm/cmark.git

+ 27 - 0
CocoaMarkdown.podspec

@@ -0,0 +1,27 @@
+Pod::Spec.new do |s|
+
+  s.name          = 'CocoaMarkdown'
+  s.version       = '1.0'
+  s.summary       = 'Markdown parsing and rendering for iOS and OS X'
+  s.description   = "CocoaMarkdown aims to solve two primary problems better than existing libraries:
+More flexibility. CocoaMarkdown allows you to define custom parsing hooks or even traverse the Markdown AST using the low-level API.
+Efficient NSAttributedString creation for easy rendering on iOS and OS X. Most existing libraries just generate HTML from the Markdown, which is not a convenient representation to work with in native apps."
+
+  s.homepage      = 'https://github.com/indragiek/CocoaMarkdown'
+  s.license       = 'MIT'
+
+  s.author        = "Indragie Karunaratne"
+  s.ios.deployment_target = '8.0'
+  s.osx.deployment_target = '10.10'
+
+  s.source        = { :git => 'https://github.com/indragiek/CocoaMarkdown.git' }
+  s.source_files  = 'CocoaMarkdown'
+  s.private_header_files = 'CocoaMarkdown/*_Private.h'
+  s.ios.framework = 'UIKit'
+  s.osx.framework = 'Cocoa'
+  s.requires_arc  = true
+
+  s.dependency 'cmark', '~> 0.21.0'
+  s.dependency 'Ono', '~> 1.1.3'
+
+end

+ 1925 - 0
CocoaMarkdown.xcodeproj/project.pbxproj

@@ -0,0 +1,1925 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		06D9F8771A70465700C00B67 /* CMHTMLUnderlineTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 06D9F8751A70465700C00B67 /* CMHTMLUnderlineTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		06D9F8781A70465700C00B67 /* CMHTMLUnderlineTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 06D9F8751A70465700C00B67 /* CMHTMLUnderlineTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		06D9F8791A70465700C00B67 /* CMHTMLUnderlineTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 06D9F8761A70465700C00B67 /* CMHTMLUnderlineTransformer.m */; };
+		06D9F87A1A70465700C00B67 /* CMHTMLUnderlineTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 06D9F8761A70465700C00B67 /* CMHTMLUnderlineTransformer.m */; };
+		30A07BCC1B5545A50097CEF1 /* render.c in Sources */ = {isa = PBXBuildFile; fileRef = 30A07BCB1B5545A50097CEF1 /* render.c */; };
+		30A07BCD1B5545A50097CEF1 /* render.c in Sources */ = {isa = PBXBuildFile; fileRef = 30A07BCB1B5545A50097CEF1 /* render.c */; };
+		720DA5811A68A00900DD05AF /* CocoaMarkdown.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 729F53FB1A68649200CC7448 /* CocoaMarkdown.framework */; };
+		720DA5821A68A01600DD05AF /* CocoaMarkdown.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 729F53FB1A68649200CC7448 /* CocoaMarkdown.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+		722694BC1A687B4100E1D3DC /* test.md in Resources */ = {isa = PBXBuildFile; fileRef = 72FE7F171A686AC300F23F46 /* test.md */; };
+		722694BD1A687BD400E1D3DC /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 729F54981A68663400CC7448 /* Nimble.framework */; };
+		722694BE1A687BD800E1D3DC /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 729F54A21A68663400CC7448 /* Quick.framework */; };
+		722E33E01A68E7E4004DE919 /* CMAttributeRun.h in Headers */ = {isa = PBXBuildFile; fileRef = 722E33DE1A68E7E4004DE919 /* CMAttributeRun.h */; };
+		722E33E11A68E7E4004DE919 /* CMAttributeRun.h in Headers */ = {isa = PBXBuildFile; fileRef = 722E33DE1A68E7E4004DE919 /* CMAttributeRun.h */; };
+		722E33E21A68E7E4004DE919 /* CMAttributeRun.m in Sources */ = {isa = PBXBuildFile; fileRef = 722E33DF1A68E7E4004DE919 /* CMAttributeRun.m */; };
+		722E33E31A68E7E4004DE919 /* CMAttributeRun.m in Sources */ = {isa = PBXBuildFile; fileRef = 722E33DF1A68E7E4004DE919 /* CMAttributeRun.m */; };
+		722E33E61A68E86B004DE919 /* CMTextAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 722E33E41A68E86B004DE919 /* CMTextAttributes.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		722E33E71A68E86B004DE919 /* CMTextAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 722E33E41A68E86B004DE919 /* CMTextAttributes.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		722E33E81A68E86B004DE919 /* CMTextAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 722E33E51A68E86B004DE919 /* CMTextAttributes.m */; };
+		722E33E91A68E86B004DE919 /* CMTextAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 722E33E51A68E86B004DE919 /* CMTextAttributes.m */; };
+		722E33EC1A68EADA004DE919 /* CMCascadingAttributeStack.h in Headers */ = {isa = PBXBuildFile; fileRef = 722E33EA1A68EADA004DE919 /* CMCascadingAttributeStack.h */; };
+		722E33ED1A68EADA004DE919 /* CMCascadingAttributeStack.h in Headers */ = {isa = PBXBuildFile; fileRef = 722E33EA1A68EADA004DE919 /* CMCascadingAttributeStack.h */; };
+		722E33EE1A68EADA004DE919 /* CMCascadingAttributeStack.m in Sources */ = {isa = PBXBuildFile; fileRef = 722E33EB1A68EADA004DE919 /* CMCascadingAttributeStack.m */; };
+		722E33EF1A68EADA004DE919 /* CMCascadingAttributeStack.m in Sources */ = {isa = PBXBuildFile; fileRef = 722E33EB1A68EADA004DE919 /* CMCascadingAttributeStack.m */; };
+		725115E01A6970A300337419 /* CMStack.h in Headers */ = {isa = PBXBuildFile; fileRef = 725115DE1A6970A300337419 /* CMStack.h */; };
+		725115E11A6970A300337419 /* CMStack.h in Headers */ = {isa = PBXBuildFile; fileRef = 725115DE1A6970A300337419 /* CMStack.h */; };
+		725115E21A6970A300337419 /* CMStack.m in Sources */ = {isa = PBXBuildFile; fileRef = 725115DF1A6970A300337419 /* CMStack.m */; };
+		725115E31A6970A300337419 /* CMStack.m in Sources */ = {isa = PBXBuildFile; fileRef = 725115DF1A6970A300337419 /* CMStack.m */; };
+		725115F51A6972AE00337419 /* CMHTMLElementTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 725115F41A6972AE00337419 /* CMHTMLElementTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		725115F61A6972AE00337419 /* CMHTMLElementTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 725115F41A6972AE00337419 /* CMHTMLElementTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		725115FC1A69735400337419 /* Ono.h in Headers */ = {isa = PBXBuildFile; fileRef = 725115F91A69735400337419 /* Ono.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		725115FD1A69735400337419 /* Ono.h in Headers */ = {isa = PBXBuildFile; fileRef = 725115F91A69735400337419 /* Ono.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		725115FE1A69735400337419 /* ONOXMLDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = 725115FA1A69735400337419 /* ONOXMLDocument.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		725115FF1A69735400337419 /* ONOXMLDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = 725115FA1A69735400337419 /* ONOXMLDocument.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		725116001A69735400337419 /* ONOXMLDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 725115FB1A69735400337419 /* ONOXMLDocument.m */; };
+		725116011A69735400337419 /* ONOXMLDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 725115FB1A69735400337419 /* ONOXMLDocument.m */; };
+		725116031A6973A700337419 /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 725116021A6973A700337419 /* libxml2.dylib */; };
+		725116051A6973AC00337419 /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 725116041A6973AC00337419 /* libxml2.dylib */; };
+		725116091A69760A00337419 /* CMHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 725116071A69760A00337419 /* CMHTMLElement.h */; };
+		7251160A1A69760A00337419 /* CMHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 725116071A69760A00337419 /* CMHTMLElement.h */; };
+		7251160B1A69760A00337419 /* CMHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 725116081A69760A00337419 /* CMHTMLElement.m */; };
+		7251160C1A69760A00337419 /* CMHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 725116081A69760A00337419 /* CMHTMLElement.m */; };
+		725A5FEB1AEC5F9F00D6342C /* blocks.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FDA1AEC5F9F00D6342C /* blocks.c */; };
+		725A5FEC1AEC5F9F00D6342C /* blocks.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FDA1AEC5F9F00D6342C /* blocks.c */; };
+		725A5FED1AEC5F9F00D6342C /* buffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FDB1AEC5F9F00D6342C /* buffer.c */; };
+		725A5FEE1AEC5F9F00D6342C /* buffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FDB1AEC5F9F00D6342C /* buffer.c */; };
+		725A5FEF1AEC5F9F00D6342C /* cmark_ctype.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FDC1AEC5F9F00D6342C /* cmark_ctype.c */; };
+		725A5FF01AEC5F9F00D6342C /* cmark_ctype.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FDC1AEC5F9F00D6342C /* cmark_ctype.c */; };
+		725A5FF11AEC5F9F00D6342C /* cmark.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FDD1AEC5F9F00D6342C /* cmark.c */; };
+		725A5FF21AEC5F9F00D6342C /* cmark.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FDD1AEC5F9F00D6342C /* cmark.c */; };
+		725A5FF51AEC5F9F00D6342C /* houdini_href_e.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FDF1AEC5F9F00D6342C /* houdini_href_e.c */; };
+		725A5FF61AEC5F9F00D6342C /* houdini_href_e.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FDF1AEC5F9F00D6342C /* houdini_href_e.c */; };
+		725A5FF71AEC5F9F00D6342C /* houdini_html_e.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE01AEC5F9F00D6342C /* houdini_html_e.c */; };
+		725A5FF81AEC5F9F00D6342C /* houdini_html_e.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE01AEC5F9F00D6342C /* houdini_html_e.c */; };
+		725A5FF91AEC5F9F00D6342C /* houdini_html_u.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE11AEC5F9F00D6342C /* houdini_html_u.c */; };
+		725A5FFA1AEC5F9F00D6342C /* houdini_html_u.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE11AEC5F9F00D6342C /* houdini_html_u.c */; };
+		725A5FFB1AEC5F9F00D6342C /* html.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE21AEC5F9F00D6342C /* html.c */; };
+		725A5FFC1AEC5F9F00D6342C /* html.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE21AEC5F9F00D6342C /* html.c */; };
+		725A5FFD1AEC5F9F00D6342C /* inlines.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE31AEC5F9F00D6342C /* inlines.c */; };
+		725A5FFE1AEC5F9F00D6342C /* inlines.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE31AEC5F9F00D6342C /* inlines.c */; };
+		725A5FFF1AEC5F9F00D6342C /* iterator.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE41AEC5F9F00D6342C /* iterator.c */; };
+		725A60001AEC5F9F00D6342C /* iterator.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE41AEC5F9F00D6342C /* iterator.c */; };
+		725A60011AEC5F9F00D6342C /* man.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE51AEC5F9F00D6342C /* man.c */; };
+		725A60021AEC5F9F00D6342C /* man.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE51AEC5F9F00D6342C /* man.c */; };
+		725A60031AEC5F9F00D6342C /* node.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE61AEC5F9F00D6342C /* node.c */; };
+		725A60041AEC5F9F00D6342C /* node.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE61AEC5F9F00D6342C /* node.c */; };
+		725A60051AEC5F9F00D6342C /* references.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE71AEC5F9F00D6342C /* references.c */; };
+		725A60061AEC5F9F00D6342C /* references.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE71AEC5F9F00D6342C /* references.c */; };
+		725A60071AEC5F9F00D6342C /* scanners.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE81AEC5F9F00D6342C /* scanners.c */; };
+		725A60081AEC5F9F00D6342C /* scanners.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE81AEC5F9F00D6342C /* scanners.c */; };
+		725A60091AEC5F9F00D6342C /* utf8.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE91AEC5F9F00D6342C /* utf8.c */; };
+		725A600A1AEC5F9F00D6342C /* utf8.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FE91AEC5F9F00D6342C /* utf8.c */; };
+		725A600B1AEC5F9F00D6342C /* xml.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FEA1AEC5F9F00D6342C /* xml.c */; };
+		725A600C1AEC5F9F00D6342C /* xml.c in Sources */ = {isa = PBXBuildFile; fileRef = 725A5FEA1AEC5F9F00D6342C /* xml.c */; };
+		72736B671A7B467E0046B957 /* CocoaMarkdown.h in Headers */ = {isa = PBXBuildFile; fileRef = 729F53DF1A68641400CC7448 /* CocoaMarkdown.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		72772BD61A699ACB0059936C /* CMHTMLUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 72772BD41A699ACA0059936C /* CMHTMLUtilities.h */; };
+		72772BD71A699ACB0059936C /* CMHTMLUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 72772BD41A699ACA0059936C /* CMHTMLUtilities.h */; };
+		72772BD81A699ACB0059936C /* CMHTMLUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 72772BD51A699ACA0059936C /* CMHTMLUtilities.m */; };
+		72772BD91A699ACB0059936C /* CMHTMLUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 72772BD51A699ACA0059936C /* CMHTMLUtilities.m */; };
+		72772BE61A69ABB20059936C /* CMHTMLStrikethroughTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 72772BE41A69ABB20059936C /* CMHTMLStrikethroughTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		72772BE71A69ABB20059936C /* CMHTMLStrikethroughTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 72772BE41A69ABB20059936C /* CMHTMLStrikethroughTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		72772BE81A69ABB20059936C /* CMHTMLStrikethroughTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 72772BE51A69ABB20059936C /* CMHTMLStrikethroughTransformer.m */; };
+		72772BE91A69ABB20059936C /* CMHTMLStrikethroughTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 72772BE51A69ABB20059936C /* CMHTMLStrikethroughTransformer.m */; };
+		72772BEC1A6A22720059936C /* CMHTMLSuperscriptTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 72772BEA1A6A22720059936C /* CMHTMLSuperscriptTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		72772BED1A6A22720059936C /* CMHTMLSuperscriptTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 72772BEA1A6A22720059936C /* CMHTMLSuperscriptTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		72772BEE1A6A22720059936C /* CMHTMLSuperscriptTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 72772BEB1A6A22720059936C /* CMHTMLSuperscriptTransformer.m */; };
+		72772BEF1A6A22720059936C /* CMHTMLSuperscriptTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 72772BEB1A6A22720059936C /* CMHTMLSuperscriptTransformer.m */; };
+		72772BF21A6A28A70059936C /* CMHTMLScriptTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 72772BF01A6A28A70059936C /* CMHTMLScriptTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		72772BF31A6A28A70059936C /* CMHTMLScriptTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 72772BF01A6A28A70059936C /* CMHTMLScriptTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		72772BF41A6A28A70059936C /* CMHTMLScriptTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 72772BF11A6A28A70059936C /* CMHTMLScriptTransformer.m */; };
+		72772BF51A6A28A70059936C /* CMHTMLScriptTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 72772BF11A6A28A70059936C /* CMHTMLScriptTransformer.m */; };
+		72772BF71A6A29810059936C /* CMHTMLScriptTransformer_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 72772BF61A6A29810059936C /* CMHTMLScriptTransformer_Private.h */; };
+		72772BF81A6A29810059936C /* CMHTMLScriptTransformer_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 72772BF61A6A29810059936C /* CMHTMLScriptTransformer_Private.h */; };
+		72772BFB1A6A2B5C0059936C /* CMHTMLSubscriptTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 72772BF91A6A2B5C0059936C /* CMHTMLSubscriptTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		72772BFC1A6A2B5C0059936C /* CMHTMLSubscriptTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 72772BF91A6A2B5C0059936C /* CMHTMLSubscriptTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		72772BFD1A6A2B5C0059936C /* CMHTMLSubscriptTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 72772BFA1A6A2B5C0059936C /* CMHTMLSubscriptTransformer.m */; };
+		72772BFE1A6A2B5C0059936C /* CMHTMLSubscriptTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 72772BFA1A6A2B5C0059936C /* CMHTMLSubscriptTransformer.m */; };
+		728FA12F1AEED87900C7A368 /* CMDocument_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 728FA12E1AEED87900C7A368 /* CMDocument_Private.h */; };
+		728FA1301AEED87900C7A368 /* CMDocument_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 728FA12E1AEED87900C7A368 /* CMDocument_Private.h */; };
+		729F53E01A68641400CC7448 /* CocoaMarkdown.h in Headers */ = {isa = PBXBuildFile; fileRef = 729F53DF1A68641400CC7448 /* CocoaMarkdown.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		729F53E61A68641400CC7448 /* CocoaMarkdown.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 729F53DA1A68641400CC7448 /* CocoaMarkdown.framework */; };
+		729F54221A6864F400CC7448 /* CMAttributedStringRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = 729F54141A6864F400CC7448 /* CMAttributedStringRenderer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		729F54231A6864F400CC7448 /* CMAttributedStringRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = 729F54141A6864F400CC7448 /* CMAttributedStringRenderer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		729F54241A6864F400CC7448 /* CMAttributedStringRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F54151A6864F400CC7448 /* CMAttributedStringRenderer.m */; };
+		729F54251A6864F400CC7448 /* CMAttributedStringRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F54151A6864F400CC7448 /* CMAttributedStringRenderer.m */; };
+		729F54261A6864F400CC7448 /* CMDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = 729F54161A6864F400CC7448 /* CMDocument.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		729F54271A6864F400CC7448 /* CMDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = 729F54161A6864F400CC7448 /* CMDocument.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		729F54281A6864F400CC7448 /* CMDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F54171A6864F400CC7448 /* CMDocument.m */; };
+		729F54291A6864F400CC7448 /* CMDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F54171A6864F400CC7448 /* CMDocument.m */; };
+		729F542A1A6864F400CC7448 /* CMIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = 729F54181A6864F400CC7448 /* CMIterator.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		729F542B1A6864F400CC7448 /* CMIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = 729F54181A6864F400CC7448 /* CMIterator.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		729F542C1A6864F400CC7448 /* CMIterator.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F54191A6864F400CC7448 /* CMIterator.m */; };
+		729F542D1A6864F400CC7448 /* CMIterator.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F54191A6864F400CC7448 /* CMIterator.m */; };
+		729F542E1A6864F400CC7448 /* CMNode_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 729F541A1A6864F400CC7448 /* CMNode_Private.h */; };
+		729F542F1A6864F400CC7448 /* CMNode_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 729F541A1A6864F400CC7448 /* CMNode_Private.h */; };
+		729F54301A6864F400CC7448 /* CMNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 729F541B1A6864F400CC7448 /* CMNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		729F54311A6864F400CC7448 /* CMNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 729F541B1A6864F400CC7448 /* CMNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		729F54321A6864F400CC7448 /* CMNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F541C1A6864F400CC7448 /* CMNode.m */; };
+		729F54331A6864F400CC7448 /* CMNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F541C1A6864F400CC7448 /* CMNode.m */; };
+		729F54341A6864F400CC7448 /* CMParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 729F541D1A6864F400CC7448 /* CMParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		729F54351A6864F400CC7448 /* CMParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 729F541D1A6864F400CC7448 /* CMParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		729F54361A6864F400CC7448 /* CMParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F541E1A6864F400CC7448 /* CMParser.m */; };
+		729F54371A6864F400CC7448 /* CMParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F541E1A6864F400CC7448 /* CMParser.m */; };
+		729F54381A6864F400CC7448 /* CMPlatformDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 729F541F1A6864F400CC7448 /* CMPlatformDefines.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		729F54391A6864F400CC7448 /* CMPlatformDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 729F541F1A6864F400CC7448 /* CMPlatformDefines.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		729F54AE1A68665200CC7448 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 729F54941A68663400CC7448 /* Nimble.framework */; };
+		729F54B21A6866C200CC7448 /* DummySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 729F54B11A6866C200CC7448 /* DummySpec.swift */; };
+		729F54B31A6866C200CC7448 /* DummySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 729F54B11A6866C200CC7448 /* DummySpec.swift */; };
+		729F54BD1A68673800CC7448 /* CMDocumentSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F54B71A68673800CC7448 /* CMDocumentSpec.m */; };
+		729F54BE1A68673800CC7448 /* CMDocumentSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F54B71A68673800CC7448 /* CMDocumentSpec.m */; };
+		729F54BF1A68673800CC7448 /* CMIteratorSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F54B81A68673800CC7448 /* CMIteratorSpec.m */; };
+		729F54C01A68673800CC7448 /* CMIteratorSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F54B81A68673800CC7448 /* CMIteratorSpec.m */; };
+		729F54C11A68673800CC7448 /* CMNodeSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F54B91A68673800CC7448 /* CMNodeSpec.m */; };
+		729F54C21A68673800CC7448 /* CMNodeSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F54B91A68673800CC7448 /* CMNodeSpec.m */; };
+		729F54C31A68673800CC7448 /* CMParserSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F54BA1A68673800CC7448 /* CMParserSpec.m */; };
+		729F54C41A68673800CC7448 /* CMParserSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F54BA1A68673800CC7448 /* CMParserSpec.m */; };
+		729F54C51A68673800CC7448 /* CMParserTestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F54BC1A68673800CC7448 /* CMParserTestObject.m */; };
+		729F54C61A68673800CC7448 /* CMParserTestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 729F54BC1A68673800CC7448 /* CMParserTestObject.m */; };
+		72C17A931A6EBA3300212F17 /* CMHTMLRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = 72C17A911A6EBA3300212F17 /* CMHTMLRenderer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		72C17A941A6EBA3300212F17 /* CMHTMLRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = 72C17A911A6EBA3300212F17 /* CMHTMLRenderer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		72C17A951A6EBA3300212F17 /* CMHTMLRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 72C17A921A6EBA3300212F17 /* CMHTMLRenderer.m */; };
+		72C17A961A6EBA3300212F17 /* CMHTMLRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 72C17A921A6EBA3300212F17 /* CMHTMLRenderer.m */; };
+		72C17A991A6EBD7800212F17 /* CMDocument+HTMLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 72C17A971A6EBD7800212F17 /* CMDocument+HTMLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		72C17A9A1A6EBD7800212F17 /* CMDocument+HTMLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 72C17A971A6EBD7800212F17 /* CMDocument+HTMLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		72C17A9B1A6EBD7800212F17 /* CMDocument+HTMLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 72C17A981A6EBD7800212F17 /* CMDocument+HTMLAdditions.m */; };
+		72C17A9C1A6EBD7800212F17 /* CMDocument+HTMLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 72C17A981A6EBD7800212F17 /* CMDocument+HTMLAdditions.m */; };
+		72C17A9E1A6EBE5600212F17 /* CMHTMLRendererSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 72C17A9D1A6EBE5600212F17 /* CMHTMLRendererSpec.m */; };
+		72C17A9F1A6EBE5600212F17 /* CMHTMLRendererSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 72C17A9D1A6EBE5600212F17 /* CMHTMLRendererSpec.m */; };
+		72FE7E9B1A68684000F23F46 /* test.md in Resources */ = {isa = PBXBuildFile; fileRef = 72FE7E981A68683300F23F46 /* test.md */; };
+		72FE7E9C1A68684700F23F46 /* test.md in Resources */ = {isa = PBXBuildFile; fileRef = 72FE7E981A68683300F23F46 /* test.md */; };
+		72FE7ECE1A68692400F23F46 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72FE7ECD1A68692400F23F46 /* AppDelegate.swift */; };
+		72FE7ED01A68692400F23F46 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72FE7ECF1A68692400F23F46 /* ViewController.swift */; };
+		72FE7ED31A68692400F23F46 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 72FE7ED11A68692400F23F46 /* Main.storyboard */; };
+		72FE7ED51A68692400F23F46 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 72FE7ED41A68692400F23F46 /* Images.xcassets */; };
+		72FE7ED81A68692400F23F46 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 72FE7ED61A68692400F23F46 /* LaunchScreen.xib */; };
+		72FE7EED1A68693900F23F46 /* CocoaMarkdown.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 729F53DA1A68641400CC7448 /* CocoaMarkdown.framework */; };
+		72FE7EEF1A68694900F23F46 /* CocoaMarkdown.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 729F53DA1A68641400CC7448 /* CocoaMarkdown.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+		72FE7EF91A68695100F23F46 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72FE7EF81A68695100F23F46 /* AppDelegate.swift */; };
+		72FE7EFB1A68695100F23F46 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 72FE7EFA1A68695100F23F46 /* Images.xcassets */; };
+		72FE7EFE1A68695100F23F46 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 72FE7EFC1A68695100F23F46 /* MainMenu.xib */; };
+		72FE7F191A686ACD00F23F46 /* test.md in Resources */ = {isa = PBXBuildFile; fileRef = 72FE7F171A686AC300F23F46 /* test.md */; };
+		FD33B8AE2285EBC100BECE98 /* CMImageTextAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = FD33B8AC2285EBC100BECE98 /* CMImageTextAttachment.h */; };
+		FD33B8AF2285EBC100BECE98 /* CMImageTextAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = FD33B8AC2285EBC100BECE98 /* CMImageTextAttachment.h */; };
+		FD33B8B02285EBC100BECE98 /* CMImageTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = FD33B8AD2285EBC100BECE98 /* CMImageTextAttachment.m */; };
+		FD33B8B12285EBC100BECE98 /* CMImageTextAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = FD33B8AD2285EBC100BECE98 /* CMImageTextAttachment.m */; };
+		FD33B8CD2288387D00BECE98 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 729F54A81A68663400CC7448 /* Quick.framework */; };
+		FD33B8F92289626800BECE98 /* CocoaMarkdown.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 729F53FB1A68649200CC7448 /* CocoaMarkdown.framework */; };
+		FD6168A72284BD4700517B13 /* CMDocument+AttributedStringAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = FD6168A42284BD4700517B13 /* CMDocument+AttributedStringAdditions.m */; };
+		FD6168A82284BD4700517B13 /* CMDocument+AttributedStringAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = FD6168A42284BD4700517B13 /* CMDocument+AttributedStringAdditions.m */; };
+		FD6168A92284BDC800517B13 /* CMDocument+AttributedStringAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = FD6168942284BD4700517B13 /* CMDocument+AttributedStringAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		FD6168AA2284BDD500517B13 /* CMDocument+AttributedStringAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = FD6168942284BD4700517B13 /* CMDocument+AttributedStringAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		720DA57F1A68A00300DD05AF /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F53D11A68641300CC7448 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 729F53FA1A68649200CC7448;
+			remoteInfo = "CocoaMarkdown-Mac";
+		};
+		729F53E71A68641400CC7448 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F53D11A68641300CC7448 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 729F53D91A68641400CC7448;
+			remoteInfo = CocoaMarkdown;
+		};
+		729F54071A68649200CC7448 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F53D11A68641300CC7448 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 729F53FA1A68649200CC7448;
+			remoteInfo = CocoaMarkdown;
+		};
+		729F54931A68663400CC7448 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F54891A68663300CC7448 /* Nimble.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 1F1A74291940169200FFFC47;
+			remoteInfo = "Nimble-iOS";
+		};
+		729F54951A68663400CC7448 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F54891A68663300CC7448 /* Nimble.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 1F1A74341940169200FFFC47;
+			remoteInfo = "Nimble-iOSTests";
+		};
+		729F54971A68663400CC7448 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F54891A68663300CC7448 /* Nimble.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 1F925EAD195C0D6300ED456B;
+			remoteInfo = "Nimble-OSX";
+		};
+		729F54991A68663400CC7448 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F54891A68663300CC7448 /* Nimble.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 1F925EB7195C0D6300ED456B;
+			remoteInfo = "Nimble-OSXTests";
+		};
+		729F54A11A68663400CC7448 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F548C1A68663400CC7448 /* Quick.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = DAEB6B8E1943873100289F44;
+			remoteInfo = "Quick-OSX";
+		};
+		729F54A31A68663400CC7448 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F548C1A68663400CC7448 /* Quick.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = DAEB6B991943873100289F44;
+			remoteInfo = "Quick-OSXTests";
+		};
+		729F54A51A68663400CC7448 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F548C1A68663400CC7448 /* Quick.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = DA5663E81A4C8D8500193C88;
+			remoteInfo = "QuickFocused-OSXTests";
+		};
+		729F54A71A68663400CC7448 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F548C1A68663400CC7448 /* Quick.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 5A5D117C19473F2100F6D13D;
+			remoteInfo = "Quick-iOS";
+		};
+		729F54A91A68663400CC7448 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F548C1A68663400CC7448 /* Quick.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 5A5D118619473F2100F6D13D;
+			remoteInfo = "Quick-iOSTests";
+		};
+		729F54AB1A68663400CC7448 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F548C1A68663400CC7448 /* Quick.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = DA9876B21A4C70EB0004AA17;
+			remoteInfo = "QuickFocused-iOSTests";
+		};
+		72FE7EEB1A68693200F23F46 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F53D11A68641300CC7448 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 729F53D91A68641400CC7448;
+			remoteInfo = "CocoaMarkdown-iOS";
+		};
+		900C08481F1E18CC0046FB99 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F54891A68663300CC7448 /* Nimble.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 1F5DF1551BDCA0CE00C3A531;
+			remoteInfo = "Nimble-tvOS";
+		};
+		900C084A1F1E18CC0046FB99 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F54891A68663300CC7448 /* Nimble.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 1F5DF15E1BDCA0CE00C3A531;
+			remoteInfo = "Nimble-tvOSTests";
+		};
+		900C08551F1E18CC0046FB99 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F548C1A68663400CC7448 /* Quick.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 1F118CD51BDCA4AB005013A2;
+			remoteInfo = "Quick-tvOS";
+		};
+		900C08571F1E18CC0046FB99 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F548C1A68663400CC7448 /* Quick.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 1F118CDE1BDCA4AB005013A2;
+			remoteInfo = "Quick-tvOSTests";
+		};
+		900C08591F1E18CC0046FB99 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F548C1A68663400CC7448 /* Quick.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 1F118CF01BDCA4BB005013A2;
+			remoteInfo = "QuickFocused-tvOSTests";
+		};
+		FD33B8C6228831CD00BECE98 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F548C1A68663400CC7448 /* Quick.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 64076CF51D6D7C2000E2B499;
+			remoteInfo = "QuickAfterSuite - macOSTests";
+		};
+		FD33B8C8228831CD00BECE98 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F548C1A68663400CC7448 /* Quick.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 64076D081D6D7CD600E2B499;
+			remoteInfo = "QuickAfterSuite - iOSTests";
+		};
+		FD33B8CA228831CD00BECE98 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 729F548C1A68663400CC7448 /* Quick.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 64076D1A1D6D7CEA00E2B499;
+			remoteInfo = "QuickAfterSuite - tvOSTests";
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		72FE7EEE1A68693E00F23F46 /* Copy Frameworks */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+				72FE7EEF1A68694900F23F46 /* CocoaMarkdown.framework in Copy Frameworks */,
+			);
+			name = "Copy Frameworks";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		72FE7F141A68697E00F23F46 /* Copy Frameworks */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+				720DA5821A68A01600DD05AF /* CocoaMarkdown.framework in Copy Frameworks */,
+			);
+			name = "Copy Frameworks";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		06D9F8751A70465700C00B67 /* CMHTMLUnderlineTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMHTMLUnderlineTransformer.h; sourceTree = "<group>"; };
+		06D9F8761A70465700C00B67 /* CMHTMLUnderlineTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMHTMLUnderlineTransformer.m; sourceTree = "<group>"; };
+		30A07BCB1B5545A50097CEF1 /* render.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = render.c; path = External/cmark/src/render.c; sourceTree = "<group>"; };
+		722E33DE1A68E7E4004DE919 /* CMAttributeRun.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMAttributeRun.h; sourceTree = "<group>"; };
+		722E33DF1A68E7E4004DE919 /* CMAttributeRun.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMAttributeRun.m; sourceTree = "<group>"; };
+		722E33E41A68E86B004DE919 /* CMTextAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMTextAttributes.h; sourceTree = "<group>"; };
+		722E33E51A68E86B004DE919 /* CMTextAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMTextAttributes.m; sourceTree = "<group>"; };
+		722E33EA1A68EADA004DE919 /* CMCascadingAttributeStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMCascadingAttributeStack.h; sourceTree = "<group>"; };
+		722E33EB1A68EADA004DE919 /* CMCascadingAttributeStack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMCascadingAttributeStack.m; sourceTree = "<group>"; };
+		725115DE1A6970A300337419 /* CMStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMStack.h; sourceTree = "<group>"; };
+		725115DF1A6970A300337419 /* CMStack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMStack.m; sourceTree = "<group>"; };
+		725115F41A6972AE00337419 /* CMHTMLElementTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMHTMLElementTransformer.h; sourceTree = "<group>"; };
+		725115F91A69735400337419 /* Ono.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ono.h; path = External/Ono/Ono/Ono.h; sourceTree = "<group>"; };
+		725115FA1A69735400337419 /* ONOXMLDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ONOXMLDocument.h; path = External/Ono/Ono/ONOXMLDocument.h; sourceTree = "<group>"; };
+		725115FB1A69735400337419 /* ONOXMLDocument.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ONOXMLDocument.m; path = External/Ono/Ono/ONOXMLDocument.m; sourceTree = "<group>"; };
+		725116021A6973A700337419 /* libxml2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.dylib; path = usr/lib/libxml2.dylib; sourceTree = SDKROOT; };
+		725116041A6973AC00337419 /* libxml2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.dylib; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib/libxml2.dylib; sourceTree = DEVELOPER_DIR; };
+		725116071A69760A00337419 /* CMHTMLElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMHTMLElement.h; sourceTree = "<group>"; };
+		725116081A69760A00337419 /* CMHTMLElement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMHTMLElement.m; sourceTree = "<group>"; };
+		725A5FDA1AEC5F9F00D6342C /* blocks.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = blocks.c; path = External/cmark/src/blocks.c; sourceTree = "<group>"; };
+		725A5FDB1AEC5F9F00D6342C /* buffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = buffer.c; path = External/cmark/src/buffer.c; sourceTree = "<group>"; };
+		725A5FDC1AEC5F9F00D6342C /* cmark_ctype.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cmark_ctype.c; path = External/cmark/src/cmark_ctype.c; sourceTree = "<group>"; };
+		725A5FDD1AEC5F9F00D6342C /* cmark.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cmark.c; path = External/cmark/src/cmark.c; sourceTree = "<group>"; };
+		725A5FDF1AEC5F9F00D6342C /* houdini_href_e.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = houdini_href_e.c; path = External/cmark/src/houdini_href_e.c; sourceTree = "<group>"; };
+		725A5FE01AEC5F9F00D6342C /* houdini_html_e.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = houdini_html_e.c; path = External/cmark/src/houdini_html_e.c; sourceTree = "<group>"; };
+		725A5FE11AEC5F9F00D6342C /* houdini_html_u.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = houdini_html_u.c; path = External/cmark/src/houdini_html_u.c; sourceTree = "<group>"; };
+		725A5FE21AEC5F9F00D6342C /* html.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = html.c; path = External/cmark/src/html.c; sourceTree = "<group>"; };
+		725A5FE31AEC5F9F00D6342C /* inlines.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = inlines.c; path = External/cmark/src/inlines.c; sourceTree = "<group>"; };
+		725A5FE41AEC5F9F00D6342C /* iterator.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = iterator.c; path = External/cmark/src/iterator.c; sourceTree = "<group>"; };
+		725A5FE51AEC5F9F00D6342C /* man.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = man.c; path = External/cmark/src/man.c; sourceTree = "<group>"; };
+		725A5FE61AEC5F9F00D6342C /* node.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = node.c; path = External/cmark/src/node.c; sourceTree = "<group>"; };
+		725A5FE71AEC5F9F00D6342C /* references.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = references.c; path = External/cmark/src/references.c; sourceTree = "<group>"; };
+		725A5FE81AEC5F9F00D6342C /* scanners.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = scanners.c; path = External/cmark/src/scanners.c; sourceTree = "<group>"; };
+		725A5FE91AEC5F9F00D6342C /* utf8.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = utf8.c; path = External/cmark/src/utf8.c; sourceTree = "<group>"; };
+		725A5FEA1AEC5F9F00D6342C /* xml.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = xml.c; path = External/cmark/src/xml.c; sourceTree = "<group>"; };
+		72772BD41A699ACA0059936C /* CMHTMLUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMHTMLUtilities.h; sourceTree = "<group>"; };
+		72772BD51A699ACA0059936C /* CMHTMLUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMHTMLUtilities.m; sourceTree = "<group>"; };
+		72772BE41A69ABB20059936C /* CMHTMLStrikethroughTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMHTMLStrikethroughTransformer.h; sourceTree = "<group>"; };
+		72772BE51A69ABB20059936C /* CMHTMLStrikethroughTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMHTMLStrikethroughTransformer.m; sourceTree = "<group>"; };
+		72772BEA1A6A22720059936C /* CMHTMLSuperscriptTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMHTMLSuperscriptTransformer.h; sourceTree = "<group>"; };
+		72772BEB1A6A22720059936C /* CMHTMLSuperscriptTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMHTMLSuperscriptTransformer.m; sourceTree = "<group>"; };
+		72772BF01A6A28A70059936C /* CMHTMLScriptTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMHTMLScriptTransformer.h; sourceTree = "<group>"; };
+		72772BF11A6A28A70059936C /* CMHTMLScriptTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMHTMLScriptTransformer.m; sourceTree = "<group>"; };
+		72772BF61A6A29810059936C /* CMHTMLScriptTransformer_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMHTMLScriptTransformer_Private.h; sourceTree = "<group>"; };
+		72772BF91A6A2B5C0059936C /* CMHTMLSubscriptTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMHTMLSubscriptTransformer.h; sourceTree = "<group>"; };
+		72772BFA1A6A2B5C0059936C /* CMHTMLSubscriptTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMHTMLSubscriptTransformer.m; sourceTree = "<group>"; };
+		728FA12E1AEED87900C7A368 /* CMDocument_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMDocument_Private.h; sourceTree = "<group>"; };
+		729F53DA1A68641400CC7448 /* CocoaMarkdown.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CocoaMarkdown.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		729F53DE1A68641400CC7448 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		729F53DF1A68641400CC7448 /* CocoaMarkdown.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CocoaMarkdown.h; sourceTree = "<group>"; };
+		729F53E51A68641400CC7448 /* CocoaMarkdownTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CocoaMarkdownTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+		729F53EB1A68641400CC7448 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		729F53FB1A68649200CC7448 /* CocoaMarkdown.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CocoaMarkdown.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		729F54051A68649200CC7448 /* CocoaMarkdownTests-Mac.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "CocoaMarkdownTests-Mac.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+		729F54141A6864F400CC7448 /* CMAttributedStringRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMAttributedStringRenderer.h; sourceTree = "<group>"; };
+		729F54151A6864F400CC7448 /* CMAttributedStringRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMAttributedStringRenderer.m; sourceTree = "<group>"; };
+		729F54161A6864F400CC7448 /* CMDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMDocument.h; sourceTree = "<group>"; };
+		729F54171A6864F400CC7448 /* CMDocument.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMDocument.m; sourceTree = "<group>"; };
+		729F54181A6864F400CC7448 /* CMIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMIterator.h; sourceTree = "<group>"; };
+		729F54191A6864F400CC7448 /* CMIterator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMIterator.m; sourceTree = "<group>"; };
+		729F541A1A6864F400CC7448 /* CMNode_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMNode_Private.h; sourceTree = "<group>"; };
+		729F541B1A6864F400CC7448 /* CMNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMNode.h; sourceTree = "<group>"; };
+		729F541C1A6864F400CC7448 /* CMNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMNode.m; sourceTree = "<group>"; };
+		729F541D1A6864F400CC7448 /* CMParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMParser.h; sourceTree = "<group>"; };
+		729F541E1A6864F400CC7448 /* CMParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMParser.m; sourceTree = "<group>"; };
+		729F541F1A6864F400CC7448 /* CMPlatformDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMPlatformDefines.h; sourceTree = "<group>"; };
+		729F54891A68663300CC7448 /* Nimble.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Nimble.xcodeproj; path = External/Nimble/Nimble.xcodeproj; sourceTree = SOURCE_ROOT; };
+		729F548C1A68663400CC7448 /* Quick.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Quick.xcodeproj; path = External/Quick/Quick.xcodeproj; sourceTree = SOURCE_ROOT; };
+		729F54B11A6866C200CC7448 /* DummySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DummySpec.swift; sourceTree = "<group>"; };
+		729F54B41A6866E200CC7448 /* CocoaMarkdownTests-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CocoaMarkdownTests-Bridging-Header.h"; sourceTree = "<group>"; };
+		729F54B71A68673800CC7448 /* CMDocumentSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMDocumentSpec.m; sourceTree = "<group>"; };
+		729F54B81A68673800CC7448 /* CMIteratorSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMIteratorSpec.m; sourceTree = "<group>"; };
+		729F54B91A68673800CC7448 /* CMNodeSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMNodeSpec.m; sourceTree = "<group>"; };
+		729F54BA1A68673800CC7448 /* CMParserSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMParserSpec.m; sourceTree = "<group>"; };
+		729F54BB1A68673800CC7448 /* CMParserTestObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMParserTestObject.h; sourceTree = "<group>"; };
+		729F54BC1A68673800CC7448 /* CMParserTestObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMParserTestObject.m; sourceTree = "<group>"; };
+		72C17A911A6EBA3300212F17 /* CMHTMLRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMHTMLRenderer.h; sourceTree = "<group>"; };
+		72C17A921A6EBA3300212F17 /* CMHTMLRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMHTMLRenderer.m; sourceTree = "<group>"; };
+		72C17A971A6EBD7800212F17 /* CMDocument+HTMLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CMDocument+HTMLAdditions.h"; sourceTree = "<group>"; };
+		72C17A981A6EBD7800212F17 /* CMDocument+HTMLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CMDocument+HTMLAdditions.m"; sourceTree = "<group>"; };
+		72C17A9D1A6EBE5600212F17 /* CMHTMLRendererSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMHTMLRendererSpec.m; sourceTree = "<group>"; };
+		72FE7E981A68683300F23F46 /* test.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = test.md; sourceTree = "<group>"; };
+		72FE7EC91A68692400F23F46 /* Example-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Example-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+		72FE7ECC1A68692400F23F46 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		72FE7ECD1A68692400F23F46 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+		72FE7ECF1A68692400F23F46 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
+		72FE7ED21A68692400F23F46 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+		72FE7ED41A68692400F23F46 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
+		72FE7ED71A68692400F23F46 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
+		72FE7EF41A68695100F23F46 /* Example-Mac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Example-Mac.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+		72FE7EF71A68695100F23F46 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		72FE7EF81A68695100F23F46 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+		72FE7EFA1A68695100F23F46 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
+		72FE7EFD1A68695100F23F46 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
+		72FE7F171A686AC300F23F46 /* test.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = test.md; sourceTree = "<group>"; };
+		FD33B8AC2285EBC100BECE98 /* CMImageTextAttachment.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CMImageTextAttachment.h; sourceTree = "<group>"; };
+		FD33B8AD2285EBC100BECE98 /* CMImageTextAttachment.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CMImageTextAttachment.m; sourceTree = "<group>"; };
+		FD6168942284BD4700517B13 /* CMDocument+AttributedStringAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CMDocument+AttributedStringAdditions.h"; sourceTree = "<group>"; };
+		FD6168A42284BD4700517B13 /* CMDocument+AttributedStringAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CMDocument+AttributedStringAdditions.m"; sourceTree = "<group>"; };
+		FD71377D228486D50039DDD0 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		729F53D61A68641400CC7448 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				725116031A6973A700337419 /* libxml2.dylib in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		729F53E21A68641400CC7448 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				729F54AE1A68665200CC7448 /* Nimble.framework in Frameworks */,
+				729F53E61A68641400CC7448 /* CocoaMarkdown.framework in Frameworks */,
+				FD33B8CD2288387D00BECE98 /* Quick.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		729F53F71A68649200CC7448 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				725116051A6973AC00337419 /* libxml2.dylib in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		729F54021A68649200CC7448 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				FD33B8F92289626800BECE98 /* CocoaMarkdown.framework in Frameworks */,
+				722694BE1A687BD800E1D3DC /* Quick.framework in Frameworks */,
+				722694BD1A687BD400E1D3DC /* Nimble.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		72FE7EC61A68692400F23F46 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				72FE7EED1A68693900F23F46 /* CocoaMarkdown.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		72FE7EF11A68695100F23F46 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				720DA5811A68A00900DD05AF /* CocoaMarkdown.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		725115F71A69733400337419 /* External */ = {
+			isa = PBXGroup;
+			children = (
+				729F543E1A68650000CC7448 /* CommonMark */,
+				725115F81A69734100337419 /* Ono */,
+			);
+			name = External;
+			sourceTree = "<group>";
+		};
+		725115F81A69734100337419 /* Ono */ = {
+			isa = PBXGroup;
+			children = (
+				725115F91A69735400337419 /* Ono.h */,
+				725115FA1A69735400337419 /* ONOXMLDocument.h */,
+				725115FB1A69735400337419 /* ONOXMLDocument.m */,
+			);
+			name = Ono;
+			sourceTree = "<group>";
+		};
+		725116061A6973B200337419 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				725116041A6973AC00337419 /* libxml2.dylib */,
+				725116021A6973A700337419 /* libxml2.dylib */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		7251160D1A69761700337419 /* Attributed Strings */ = {
+			isa = PBXGroup;
+			children = (
+				7251160F1A69762800337419 /* HTML */,
+				729F54141A6864F400CC7448 /* CMAttributedStringRenderer.h */,
+				729F54151A6864F400CC7448 /* CMAttributedStringRenderer.m */,
+				722E33DE1A68E7E4004DE919 /* CMAttributeRun.h */,
+				722E33DF1A68E7E4004DE919 /* CMAttributeRun.m */,
+				722E33EA1A68EADA004DE919 /* CMCascadingAttributeStack.h */,
+				722E33EB1A68EADA004DE919 /* CMCascadingAttributeStack.m */,
+				725115DE1A6970A300337419 /* CMStack.h */,
+				725115DF1A6970A300337419 /* CMStack.m */,
+				722E33E41A68E86B004DE919 /* CMTextAttributes.h */,
+				722E33E51A68E86B004DE919 /* CMTextAttributes.m */,
+				FD6168942284BD4700517B13 /* CMDocument+AttributedStringAdditions.h */,
+				FD6168A42284BD4700517B13 /* CMDocument+AttributedStringAdditions.m */,
+				FD33B8AC2285EBC100BECE98 /* CMImageTextAttachment.h */,
+				FD33B8AD2285EBC100BECE98 /* CMImageTextAttachment.m */,
+			);
+			name = "Attributed Strings";
+			sourceTree = "<group>";
+		};
+		7251160F1A69762800337419 /* HTML */ = {
+			isa = PBXGroup;
+			children = (
+				725116071A69760A00337419 /* CMHTMLElement.h */,
+				725116081A69760A00337419 /* CMHTMLElement.m */,
+				725115F41A6972AE00337419 /* CMHTMLElementTransformer.h */,
+				72772BD41A699ACA0059936C /* CMHTMLUtilities.h */,
+				72772BD51A699ACA0059936C /* CMHTMLUtilities.m */,
+				72772BE41A69ABB20059936C /* CMHTMLStrikethroughTransformer.h */,
+				72772BE51A69ABB20059936C /* CMHTMLStrikethroughTransformer.m */,
+				72772BEA1A6A22720059936C /* CMHTMLSuperscriptTransformer.h */,
+				72772BEB1A6A22720059936C /* CMHTMLSuperscriptTransformer.m */,
+				72772BF91A6A2B5C0059936C /* CMHTMLSubscriptTransformer.h */,
+				72772BFA1A6A2B5C0059936C /* CMHTMLSubscriptTransformer.m */,
+				72772BF01A6A28A70059936C /* CMHTMLScriptTransformer.h */,
+				72772BF11A6A28A70059936C /* CMHTMLScriptTransformer.m */,
+				72772BF61A6A29810059936C /* CMHTMLScriptTransformer_Private.h */,
+				06D9F8751A70465700C00B67 /* CMHTMLUnderlineTransformer.h */,
+				06D9F8761A70465700C00B67 /* CMHTMLUnderlineTransformer.m */,
+			);
+			name = HTML;
+			sourceTree = "<group>";
+		};
+		729F53D01A68641300CC7448 = {
+			isa = PBXGroup;
+			children = (
+				725115F71A69733400337419 /* External */,
+				729F53DC1A68641400CC7448 /* CocoaMarkdown */,
+				729F53E91A68641400CC7448 /* CocoaMarkdownTests */,
+				72FE7ECA1A68692400F23F46 /* Example-iOS */,
+				72FE7EF51A68695100F23F46 /* Example-Mac */,
+				FD71377D228486D50039DDD0 /* README.md */,
+				729F53DB1A68641400CC7448 /* Products */,
+				FD33B8CC2288387D00BECE98 /* Frameworks */,
+			);
+			sourceTree = "<group>";
+		};
+		729F53DB1A68641400CC7448 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				729F53DA1A68641400CC7448 /* CocoaMarkdown.framework */,
+				729F53E51A68641400CC7448 /* CocoaMarkdownTests-iOS.xctest */,
+				729F53FB1A68649200CC7448 /* CocoaMarkdown.framework */,
+				729F54051A68649200CC7448 /* CocoaMarkdownTests-Mac.xctest */,
+				72FE7EC91A68692400F23F46 /* Example-iOS.app */,
+				72FE7EF41A68695100F23F46 /* Example-Mac.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		729F53DC1A68641400CC7448 /* CocoaMarkdown */ = {
+			isa = PBXGroup;
+			children = (
+				729F53DF1A68641400CC7448 /* CocoaMarkdown.h */,
+				729F541F1A6864F400CC7448 /* CMPlatformDefines.h */,
+				72C17A8F1A6EB9AB00212F17 /* CommonMark */,
+				7251160D1A69761700337419 /* Attributed Strings */,
+				72C17A901A6EBA1B00212F17 /* HTML */,
+				729F53DD1A68641400CC7448 /* Supporting Files */,
+				725116061A6973B200337419 /* Frameworks */,
+			);
+			path = CocoaMarkdown;
+			sourceTree = "<group>";
+		};
+		729F53DD1A68641400CC7448 /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				729F53DE1A68641400CC7448 /* Info.plist */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+		729F53E91A68641400CC7448 /* CocoaMarkdownTests */ = {
+			isa = PBXGroup;
+			children = (
+				729F54B71A68673800CC7448 /* CMDocumentSpec.m */,
+				729F54B81A68673800CC7448 /* CMIteratorSpec.m */,
+				729F54B91A68673800CC7448 /* CMNodeSpec.m */,
+				729F54BA1A68673800CC7448 /* CMParserSpec.m */,
+				729F54BB1A68673800CC7448 /* CMParserTestObject.h */,
+				729F54BC1A68673800CC7448 /* CMParserTestObject.m */,
+				72C17A9D1A6EBE5600212F17 /* CMHTMLRendererSpec.m */,
+				72FE7E971A68683300F23F46 /* Resources */,
+				729F54781A6865D300CC7448 /* Frameworks */,
+				729F53EA1A68641400CC7448 /* Supporting Files */,
+			);
+			path = CocoaMarkdownTests;
+			sourceTree = "<group>";
+		};
+		729F53EA1A68641400CC7448 /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				729F54B11A6866C200CC7448 /* DummySpec.swift */,
+				729F54B41A6866E200CC7448 /* CocoaMarkdownTests-Bridging-Header.h */,
+				729F53EB1A68641400CC7448 /* Info.plist */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+		729F543E1A68650000CC7448 /* CommonMark */ = {
+			isa = PBXGroup;
+			children = (
+				725A5FDA1AEC5F9F00D6342C /* blocks.c */,
+				725A5FDB1AEC5F9F00D6342C /* buffer.c */,
+				725A5FDC1AEC5F9F00D6342C /* cmark_ctype.c */,
+				725A5FDD1AEC5F9F00D6342C /* cmark.c */,
+				725A5FDF1AEC5F9F00D6342C /* houdini_href_e.c */,
+				725A5FE01AEC5F9F00D6342C /* houdini_html_e.c */,
+				725A5FE11AEC5F9F00D6342C /* houdini_html_u.c */,
+				725A5FE21AEC5F9F00D6342C /* html.c */,
+				725A5FE31AEC5F9F00D6342C /* inlines.c */,
+				725A5FE41AEC5F9F00D6342C /* iterator.c */,
+				725A5FE51AEC5F9F00D6342C /* man.c */,
+				725A5FE61AEC5F9F00D6342C /* node.c */,
+				725A5FE71AEC5F9F00D6342C /* references.c */,
+				30A07BCB1B5545A50097CEF1 /* render.c */,
+				725A5FE81AEC5F9F00D6342C /* scanners.c */,
+				725A5FE91AEC5F9F00D6342C /* utf8.c */,
+				725A5FEA1AEC5F9F00D6342C /* xml.c */,
+			);
+			name = CommonMark;
+			sourceTree = "<group>";
+		};
+		729F54781A6865D300CC7448 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				729F54891A68663300CC7448 /* Nimble.xcodeproj */,
+				729F548C1A68663400CC7448 /* Quick.xcodeproj */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		729F548A1A68663300CC7448 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				729F54981A68663400CC7448 /* Nimble.framework */,
+				729F549A1A68663400CC7448 /* NimbleTests.xctest */,
+				729F54941A68663400CC7448 /* Nimble.framework */,
+				729F54961A68663400CC7448 /* NimbleTests.xctest */,
+				900C08491F1E18CC0046FB99 /* Nimble.framework */,
+				900C084B1F1E18CC0046FB99 /* NimbleTests.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		729F548D1A68663400CC7448 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				729F54A21A68663400CC7448 /* Quick.framework */,
+				729F54A41A68663400CC7448 /* Quick - macOSTests.xctest */,
+				729F54A61A68663400CC7448 /* QuickFocused - macOSTests.xctest */,
+				FD33B8C7228831CD00BECE98 /* QuickAfterSuite - macOSTests.xctest */,
+				729F54A81A68663400CC7448 /* Quick.framework */,
+				729F54AA1A68663400CC7448 /* Quick - iOSTests.xctest */,
+				729F54AC1A68663400CC7448 /* QuickFocused - iOSTests.xctest */,
+				FD33B8C9228831CD00BECE98 /* QuickAfterSuite - iOSTests.xctest */,
+				900C08561F1E18CC0046FB99 /* Quick.framework */,
+				900C08581F1E18CC0046FB99 /* Quick - tvOSTests.xctest */,
+				900C085A1F1E18CC0046FB99 /* QuickFocused - tvOSTests.xctest */,
+				FD33B8CB228831CD00BECE98 /* QuickAfterSuite - tvOSTests.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		72C17A8F1A6EB9AB00212F17 /* CommonMark */ = {
+			isa = PBXGroup;
+			children = (
+				729F54161A6864F400CC7448 /* CMDocument.h */,
+				729F54171A6864F400CC7448 /* CMDocument.m */,
+				728FA12E1AEED87900C7A368 /* CMDocument_Private.h */,
+				729F54181A6864F400CC7448 /* CMIterator.h */,
+				729F54191A6864F400CC7448 /* CMIterator.m */,
+				729F541A1A6864F400CC7448 /* CMNode_Private.h */,
+				729F541B1A6864F400CC7448 /* CMNode.h */,
+				729F541C1A6864F400CC7448 /* CMNode.m */,
+				729F541D1A6864F400CC7448 /* CMParser.h */,
+				729F541E1A6864F400CC7448 /* CMParser.m */,
+			);
+			name = CommonMark;
+			sourceTree = "<group>";
+		};
+		72C17A901A6EBA1B00212F17 /* HTML */ = {
+			isa = PBXGroup;
+			children = (
+				72C17A911A6EBA3300212F17 /* CMHTMLRenderer.h */,
+				72C17A921A6EBA3300212F17 /* CMHTMLRenderer.m */,
+				72C17A971A6EBD7800212F17 /* CMDocument+HTMLAdditions.h */,
+				72C17A981A6EBD7800212F17 /* CMDocument+HTMLAdditions.m */,
+			);
+			name = HTML;
+			sourceTree = "<group>";
+		};
+		72FE7E971A68683300F23F46 /* Resources */ = {
+			isa = PBXGroup;
+			children = (
+				72FE7E981A68683300F23F46 /* test.md */,
+			);
+			path = Resources;
+			sourceTree = "<group>";
+		};
+		72FE7ECA1A68692400F23F46 /* Example-iOS */ = {
+			isa = PBXGroup;
+			children = (
+				72FE7ECD1A68692400F23F46 /* AppDelegate.swift */,
+				72FE7ECF1A68692400F23F46 /* ViewController.swift */,
+				72FE7ED11A68692400F23F46 /* Main.storyboard */,
+				72FE7ED41A68692400F23F46 /* Images.xcassets */,
+				72FE7ED61A68692400F23F46 /* LaunchScreen.xib */,
+				72FE7F161A686AC300F23F46 /* Resources */,
+				72FE7ECB1A68692400F23F46 /* Supporting Files */,
+			);
+			path = "Example-iOS";
+			sourceTree = "<group>";
+		};
+		72FE7ECB1A68692400F23F46 /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				72FE7ECC1A68692400F23F46 /* Info.plist */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+		72FE7EF51A68695100F23F46 /* Example-Mac */ = {
+			isa = PBXGroup;
+			children = (
+				72FE7EF81A68695100F23F46 /* AppDelegate.swift */,
+				72FE7EFA1A68695100F23F46 /* Images.xcassets */,
+				72FE7EFC1A68695100F23F46 /* MainMenu.xib */,
+				72FE7EF61A68695100F23F46 /* Supporting Files */,
+			);
+			path = "Example-Mac";
+			sourceTree = "<group>";
+		};
+		72FE7EF61A68695100F23F46 /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				72FE7EF71A68695100F23F46 /* Info.plist */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+		72FE7F161A686AC300F23F46 /* Resources */ = {
+			isa = PBXGroup;
+			children = (
+				72FE7F171A686AC300F23F46 /* test.md */,
+			);
+			name = Resources;
+			path = CocoaMarkdownTests/Resources;
+			sourceTree = SOURCE_ROOT;
+		};
+		FD33B8CC2288387D00BECE98 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+		729F53D71A68641400CC7448 /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				06D9F8771A70465700C00B67 /* CMHTMLUnderlineTransformer.h in Headers */,
+				722E33E61A68E86B004DE919 /* CMTextAttributes.h in Headers */,
+				FD33B8AE2285EBC100BECE98 /* CMImageTextAttachment.h in Headers */,
+				72C17A991A6EBD7800212F17 /* CMDocument+HTMLAdditions.h in Headers */,
+				729F54301A6864F400CC7448 /* CMNode.h in Headers */,
+				72772BE61A69ABB20059936C /* CMHTMLStrikethroughTransformer.h in Headers */,
+				725115E01A6970A300337419 /* CMStack.h in Headers */,
+				722E33E01A68E7E4004DE919 /* CMAttributeRun.h in Headers */,
+				729F54341A6864F400CC7448 /* CMParser.h in Headers */,
+				72772BF71A6A29810059936C /* CMHTMLScriptTransformer_Private.h in Headers */,
+				72772BFB1A6A2B5C0059936C /* CMHTMLSubscriptTransformer.h in Headers */,
+				725115FC1A69735400337419 /* Ono.h in Headers */,
+				725115FE1A69735400337419 /* ONOXMLDocument.h in Headers */,
+				72772BEC1A6A22720059936C /* CMHTMLSuperscriptTransformer.h in Headers */,
+				729F542A1A6864F400CC7448 /* CMIterator.h in Headers */,
+				FD6168AA2284BDD500517B13 /* CMDocument+AttributedStringAdditions.h in Headers */,
+				729F54261A6864F400CC7448 /* CMDocument.h in Headers */,
+				728FA12F1AEED87900C7A368 /* CMDocument_Private.h in Headers */,
+				725115F51A6972AE00337419 /* CMHTMLElementTransformer.h in Headers */,
+				72772BF21A6A28A70059936C /* CMHTMLScriptTransformer.h in Headers */,
+				729F54221A6864F400CC7448 /* CMAttributedStringRenderer.h in Headers */,
+				72772BD61A699ACB0059936C /* CMHTMLUtilities.h in Headers */,
+				725116091A69760A00337419 /* CMHTMLElement.h in Headers */,
+				72C17A931A6EBA3300212F17 /* CMHTMLRenderer.h in Headers */,
+				722E33EC1A68EADA004DE919 /* CMCascadingAttributeStack.h in Headers */,
+				729F54381A6864F400CC7448 /* CMPlatformDefines.h in Headers */,
+				729F53E01A68641400CC7448 /* CocoaMarkdown.h in Headers */,
+				729F542E1A6864F400CC7448 /* CMNode_Private.h in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		729F53F81A68649200CC7448 /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				725115F61A6972AE00337419 /* CMHTMLElementTransformer.h in Headers */,
+				729F54231A6864F400CC7448 /* CMAttributedStringRenderer.h in Headers */,
+				FD33B8AF2285EBC100BECE98 /* CMImageTextAttachment.h in Headers */,
+				725115FF1A69735400337419 /* ONOXMLDocument.h in Headers */,
+				729F54271A6864F400CC7448 /* CMDocument.h in Headers */,
+				72772BF81A6A29810059936C /* CMHTMLScriptTransformer_Private.h in Headers */,
+				729F54351A6864F400CC7448 /* CMParser.h in Headers */,
+				722E33E11A68E7E4004DE919 /* CMAttributeRun.h in Headers */,
+				722E33E71A68E86B004DE919 /* CMTextAttributes.h in Headers */,
+				06D9F8781A70465700C00B67 /* CMHTMLUnderlineTransformer.h in Headers */,
+				729F542B1A6864F400CC7448 /* CMIterator.h in Headers */,
+				729F54311A6864F400CC7448 /* CMNode.h in Headers */,
+				7251160A1A69760A00337419 /* CMHTMLElement.h in Headers */,
+				72772BFC1A6A2B5C0059936C /* CMHTMLSubscriptTransformer.h in Headers */,
+				72C17A941A6EBA3300212F17 /* CMHTMLRenderer.h in Headers */,
+				72772BD71A699ACB0059936C /* CMHTMLUtilities.h in Headers */,
+				728FA1301AEED87900C7A368 /* CMDocument_Private.h in Headers */,
+				725115E11A6970A300337419 /* CMStack.h in Headers */,
+				729F54391A6864F400CC7448 /* CMPlatformDefines.h in Headers */,
+				FD6168A92284BDC800517B13 /* CMDocument+AttributedStringAdditions.h in Headers */,
+				72772BF31A6A28A70059936C /* CMHTMLScriptTransformer.h in Headers */,
+				725115FD1A69735400337419 /* Ono.h in Headers */,
+				72736B671A7B467E0046B957 /* CocoaMarkdown.h in Headers */,
+				72C17A9A1A6EBD7800212F17 /* CMDocument+HTMLAdditions.h in Headers */,
+				72772BED1A6A22720059936C /* CMHTMLSuperscriptTransformer.h in Headers */,
+				722E33ED1A68EADA004DE919 /* CMCascadingAttributeStack.h in Headers */,
+				72772BE71A69ABB20059936C /* CMHTMLStrikethroughTransformer.h in Headers */,
+				729F542F1A6864F400CC7448 /* CMNode_Private.h in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+		729F53D91A68641400CC7448 /* CocoaMarkdown-iOS */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 729F53F01A68641400CC7448 /* Build configuration list for PBXNativeTarget "CocoaMarkdown-iOS" */;
+			buildPhases = (
+				729F53D51A68641400CC7448 /* Sources */,
+				729F53D61A68641400CC7448 /* Frameworks */,
+				729F53D71A68641400CC7448 /* Headers */,
+				729F53D81A68641400CC7448 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = "CocoaMarkdown-iOS";
+			productName = CocoaMarkdown;
+			productReference = 729F53DA1A68641400CC7448 /* CocoaMarkdown.framework */;
+			productType = "com.apple.product-type.framework";
+		};
+		729F53E41A68641400CC7448 /* CocoaMarkdownTests-iOS */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 729F53F31A68641400CC7448 /* Build configuration list for PBXNativeTarget "CocoaMarkdownTests-iOS" */;
+			buildPhases = (
+				729F53E11A68641400CC7448 /* Sources */,
+				729F53E21A68641400CC7448 /* Frameworks */,
+				729F53E31A68641400CC7448 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				729F53E81A68641400CC7448 /* PBXTargetDependency */,
+			);
+			name = "CocoaMarkdownTests-iOS";
+			productName = CocoaMarkdownTests;
+			productReference = 729F53E51A68641400CC7448 /* CocoaMarkdownTests-iOS.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+		729F53FA1A68649200CC7448 /* CocoaMarkdown-Mac */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 729F540E1A68649200CC7448 /* Build configuration list for PBXNativeTarget "CocoaMarkdown-Mac" */;
+			buildPhases = (
+				729F53F61A68649200CC7448 /* Sources */,
+				729F53F71A68649200CC7448 /* Frameworks */,
+				729F53F81A68649200CC7448 /* Headers */,
+				729F53F91A68649200CC7448 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = "CocoaMarkdown-Mac";
+			productName = CocoaMarkdown;
+			productReference = 729F53FB1A68649200CC7448 /* CocoaMarkdown.framework */;
+			productType = "com.apple.product-type.framework";
+		};
+		729F54041A68649200CC7448 /* CocoaMarkdownTests-Mac */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 729F54111A68649200CC7448 /* Build configuration list for PBXNativeTarget "CocoaMarkdownTests-Mac" */;
+			buildPhases = (
+				729F54011A68649200CC7448 /* Sources */,
+				729F54021A68649200CC7448 /* Frameworks */,
+				729F54031A68649200CC7448 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				729F54081A68649200CC7448 /* PBXTargetDependency */,
+			);
+			name = "CocoaMarkdownTests-Mac";
+			productName = CocoaMarkdownTests;
+			productReference = 729F54051A68649200CC7448 /* CocoaMarkdownTests-Mac.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+		72FE7EC81A68692400F23F46 /* Example-iOS */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 72FE7EE51A68692400F23F46 /* Build configuration list for PBXNativeTarget "Example-iOS" */;
+			buildPhases = (
+				72FE7EC51A68692400F23F46 /* Sources */,
+				72FE7EC61A68692400F23F46 /* Frameworks */,
+				72FE7EC71A68692400F23F46 /* Resources */,
+				72FE7EEE1A68693E00F23F46 /* Copy Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				72FE7EEC1A68693200F23F46 /* PBXTargetDependency */,
+			);
+			name = "Example-iOS";
+			productName = "Example-iOS";
+			productReference = 72FE7EC91A68692400F23F46 /* Example-iOS.app */;
+			productType = "com.apple.product-type.application";
+		};
+		72FE7EF31A68695100F23F46 /* Example-Mac */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 72FE7F0B1A68695100F23F46 /* Build configuration list for PBXNativeTarget "Example-Mac" */;
+			buildPhases = (
+				72FE7EF01A68695100F23F46 /* Sources */,
+				72FE7EF11A68695100F23F46 /* Frameworks */,
+				72FE7EF21A68695100F23F46 /* Resources */,
+				72FE7F141A68697E00F23F46 /* Copy Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				720DA5801A68A00300DD05AF /* PBXTargetDependency */,
+			);
+			name = "Example-Mac";
+			productName = "Example-Mac";
+			productReference = 72FE7EF41A68695100F23F46 /* Example-Mac.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		729F53D11A68641300CC7448 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastSwiftUpdateCheck = 0720;
+				LastUpgradeCheck = 1010;
+				ORGANIZATIONNAME = "Indragie Karunaratne";
+				TargetAttributes = {
+					729F53D91A68641400CC7448 = {
+						CreatedOnToolsVersion = 6.1.1;
+						LastSwiftMigration = 0830;
+					};
+					729F53E41A68641400CC7448 = {
+						CreatedOnToolsVersion = 6.1.1;
+						LastSwiftMigration = 0830;
+					};
+					729F53FA1A68649200CC7448 = {
+						CreatedOnToolsVersion = 6.1.1;
+					};
+					729F54041A68649200CC7448 = {
+						CreatedOnToolsVersion = 6.1.1;
+					};
+					72FE7EC81A68692400F23F46 = {
+						CreatedOnToolsVersion = 6.1.1;
+						ProvisioningStyle = Manual;
+					};
+					72FE7EF31A68695100F23F46 = {
+						CreatedOnToolsVersion = 6.1.1;
+					};
+				};
+			};
+			buildConfigurationList = 729F53D41A68641300CC7448 /* Build configuration list for PBXProject "CocoaMarkdown" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 729F53D01A68641300CC7448;
+			productRefGroup = 729F53DB1A68641400CC7448 /* Products */;
+			projectDirPath = "";
+			projectReferences = (
+				{
+					ProductGroup = 729F548A1A68663300CC7448 /* Products */;
+					ProjectRef = 729F54891A68663300CC7448 /* Nimble.xcodeproj */;
+				},
+				{
+					ProductGroup = 729F548D1A68663400CC7448 /* Products */;
+					ProjectRef = 729F548C1A68663400CC7448 /* Quick.xcodeproj */;
+				},
+			);
+			projectRoot = "";
+			targets = (
+				729F53D91A68641400CC7448 /* CocoaMarkdown-iOS */,
+				729F53E41A68641400CC7448 /* CocoaMarkdownTests-iOS */,
+				729F53FA1A68649200CC7448 /* CocoaMarkdown-Mac */,
+				729F54041A68649200CC7448 /* CocoaMarkdownTests-Mac */,
+				72FE7EC81A68692400F23F46 /* Example-iOS */,
+				72FE7EF31A68695100F23F46 /* Example-Mac */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXReferenceProxy section */
+		729F54941A68663400CC7448 /* Nimble.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = Nimble.framework;
+			remoteRef = 729F54931A68663400CC7448 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		729F54961A68663400CC7448 /* NimbleTests.xctest */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.cfbundle;
+			path = NimbleTests.xctest;
+			remoteRef = 729F54951A68663400CC7448 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		729F54981A68663400CC7448 /* Nimble.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = Nimble.framework;
+			remoteRef = 729F54971A68663400CC7448 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		729F549A1A68663400CC7448 /* NimbleTests.xctest */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.cfbundle;
+			path = NimbleTests.xctest;
+			remoteRef = 729F54991A68663400CC7448 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		729F54A21A68663400CC7448 /* Quick.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = Quick.framework;
+			remoteRef = 729F54A11A68663400CC7448 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		729F54A41A68663400CC7448 /* Quick - macOSTests.xctest */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.cfbundle;
+			path = "Quick - macOSTests.xctest";
+			remoteRef = 729F54A31A68663400CC7448 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		729F54A61A68663400CC7448 /* QuickFocused - macOSTests.xctest */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.cfbundle;
+			path = "QuickFocused - macOSTests.xctest";
+			remoteRef = 729F54A51A68663400CC7448 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		729F54A81A68663400CC7448 /* Quick.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = Quick.framework;
+			remoteRef = 729F54A71A68663400CC7448 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		729F54AA1A68663400CC7448 /* Quick - iOSTests.xctest */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.cfbundle;
+			path = "Quick - iOSTests.xctest";
+			remoteRef = 729F54A91A68663400CC7448 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		729F54AC1A68663400CC7448 /* QuickFocused - iOSTests.xctest */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.cfbundle;
+			path = "QuickFocused - iOSTests.xctest";
+			remoteRef = 729F54AB1A68663400CC7448 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		900C08491F1E18CC0046FB99 /* Nimble.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = Nimble.framework;
+			remoteRef = 900C08481F1E18CC0046FB99 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		900C084B1F1E18CC0046FB99 /* NimbleTests.xctest */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.cfbundle;
+			path = NimbleTests.xctest;
+			remoteRef = 900C084A1F1E18CC0046FB99 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		900C08561F1E18CC0046FB99 /* Quick.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = Quick.framework;
+			remoteRef = 900C08551F1E18CC0046FB99 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		900C08581F1E18CC0046FB99 /* Quick - tvOSTests.xctest */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.cfbundle;
+			path = "Quick - tvOSTests.xctest";
+			remoteRef = 900C08571F1E18CC0046FB99 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		900C085A1F1E18CC0046FB99 /* QuickFocused - tvOSTests.xctest */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.cfbundle;
+			path = "QuickFocused - tvOSTests.xctest";
+			remoteRef = 900C08591F1E18CC0046FB99 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		FD33B8C7228831CD00BECE98 /* QuickAfterSuite - macOSTests.xctest */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.cfbundle;
+			path = "QuickAfterSuite - macOSTests.xctest";
+			remoteRef = FD33B8C6228831CD00BECE98 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		FD33B8C9228831CD00BECE98 /* QuickAfterSuite - iOSTests.xctest */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.cfbundle;
+			path = "QuickAfterSuite - iOSTests.xctest";
+			remoteRef = FD33B8C8228831CD00BECE98 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		FD33B8CB228831CD00BECE98 /* QuickAfterSuite - tvOSTests.xctest */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.cfbundle;
+			path = "QuickAfterSuite - tvOSTests.xctest";
+			remoteRef = FD33B8CA228831CD00BECE98 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+/* End PBXReferenceProxy section */
+
+/* Begin PBXResourcesBuildPhase section */
+		729F53D81A68641400CC7448 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		729F53E31A68641400CC7448 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				72FE7E9C1A68684700F23F46 /* test.md in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		729F53F91A68649200CC7448 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		729F54031A68649200CC7448 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				72FE7E9B1A68684000F23F46 /* test.md in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		72FE7EC71A68692400F23F46 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				72FE7ED31A68692400F23F46 /* Main.storyboard in Resources */,
+				72FE7ED81A68692400F23F46 /* LaunchScreen.xib in Resources */,
+				72FE7ED51A68692400F23F46 /* Images.xcassets in Resources */,
+				72FE7F191A686ACD00F23F46 /* test.md in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		72FE7EF21A68695100F23F46 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				72FE7EFB1A68695100F23F46 /* Images.xcassets in Resources */,
+				72FE7EFE1A68695100F23F46 /* MainMenu.xib in Resources */,
+				722694BC1A687B4100E1D3DC /* test.md in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		729F53D51A68641400CC7448 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				725A60091AEC5F9F00D6342C /* utf8.c in Sources */,
+				7251160B1A69760A00337419 /* CMHTMLElement.m in Sources */,
+				729F542C1A6864F400CC7448 /* CMIterator.m in Sources */,
+				729F54361A6864F400CC7448 /* CMParser.m in Sources */,
+				725A5FF91AEC5F9F00D6342C /* houdini_html_u.c in Sources */,
+				722E33E21A68E7E4004DE919 /* CMAttributeRun.m in Sources */,
+				72772BEE1A6A22720059936C /* CMHTMLSuperscriptTransformer.m in Sources */,
+				729F54321A6864F400CC7448 /* CMNode.m in Sources */,
+				725A5FEF1AEC5F9F00D6342C /* cmark_ctype.c in Sources */,
+				72772BFD1A6A2B5C0059936C /* CMHTMLSubscriptTransformer.m in Sources */,
+				725A5FEB1AEC5F9F00D6342C /* blocks.c in Sources */,
+				725A60011AEC5F9F00D6342C /* man.c in Sources */,
+				72772BF41A6A28A70059936C /* CMHTMLScriptTransformer.m in Sources */,
+				725A60051AEC5F9F00D6342C /* references.c in Sources */,
+				725A5FF51AEC5F9F00D6342C /* houdini_href_e.c in Sources */,
+				FD33B8B02285EBC100BECE98 /* CMImageTextAttachment.m in Sources */,
+				FD6168A72284BD4700517B13 /* CMDocument+AttributedStringAdditions.m in Sources */,
+				729F54281A6864F400CC7448 /* CMDocument.m in Sources */,
+				725116001A69735400337419 /* ONOXMLDocument.m in Sources */,
+				30A07BCC1B5545A50097CEF1 /* render.c in Sources */,
+				06D9F8791A70465700C00B67 /* CMHTMLUnderlineTransformer.m in Sources */,
+				725A5FFD1AEC5F9F00D6342C /* inlines.c in Sources */,
+				72772BD81A699ACB0059936C /* CMHTMLUtilities.m in Sources */,
+				725A60031AEC5F9F00D6342C /* node.c in Sources */,
+				725115E21A6970A300337419 /* CMStack.m in Sources */,
+				725A5FED1AEC5F9F00D6342C /* buffer.c in Sources */,
+				72C17A951A6EBA3300212F17 /* CMHTMLRenderer.m in Sources */,
+				725A5FF71AEC5F9F00D6342C /* houdini_html_e.c in Sources */,
+				729F54241A6864F400CC7448 /* CMAttributedStringRenderer.m in Sources */,
+				722E33E81A68E86B004DE919 /* CMTextAttributes.m in Sources */,
+				725A600B1AEC5F9F00D6342C /* xml.c in Sources */,
+				72C17A9B1A6EBD7800212F17 /* CMDocument+HTMLAdditions.m in Sources */,
+				725A5FFF1AEC5F9F00D6342C /* iterator.c in Sources */,
+				725A60071AEC5F9F00D6342C /* scanners.c in Sources */,
+				725A5FFB1AEC5F9F00D6342C /* html.c in Sources */,
+				72772BE81A69ABB20059936C /* CMHTMLStrikethroughTransformer.m in Sources */,
+				725A5FF11AEC5F9F00D6342C /* cmark.c in Sources */,
+				722E33EE1A68EADA004DE919 /* CMCascadingAttributeStack.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		729F53E11A68641400CC7448 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				729F54C11A68673800CC7448 /* CMNodeSpec.m in Sources */,
+				729F54C51A68673800CC7448 /* CMParserTestObject.m in Sources */,
+				72C17A9E1A6EBE5600212F17 /* CMHTMLRendererSpec.m in Sources */,
+				729F54BF1A68673800CC7448 /* CMIteratorSpec.m in Sources */,
+				729F54C31A68673800CC7448 /* CMParserSpec.m in Sources */,
+				729F54B21A6866C200CC7448 /* DummySpec.swift in Sources */,
+				729F54BD1A68673800CC7448 /* CMDocumentSpec.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		729F53F61A68649200CC7448 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				725A600A1AEC5F9F00D6342C /* utf8.c in Sources */,
+				7251160C1A69760A00337419 /* CMHTMLElement.m in Sources */,
+				729F542D1A6864F400CC7448 /* CMIterator.m in Sources */,
+				729F54371A6864F400CC7448 /* CMParser.m in Sources */,
+				725A5FFA1AEC5F9F00D6342C /* houdini_html_u.c in Sources */,
+				722E33E31A68E7E4004DE919 /* CMAttributeRun.m in Sources */,
+				72772BEF1A6A22720059936C /* CMHTMLSuperscriptTransformer.m in Sources */,
+				729F54331A6864F400CC7448 /* CMNode.m in Sources */,
+				725A5FF01AEC5F9F00D6342C /* cmark_ctype.c in Sources */,
+				72772BFE1A6A2B5C0059936C /* CMHTMLSubscriptTransformer.m in Sources */,
+				725A5FEC1AEC5F9F00D6342C /* blocks.c in Sources */,
+				725A60021AEC5F9F00D6342C /* man.c in Sources */,
+				72772BF51A6A28A70059936C /* CMHTMLScriptTransformer.m in Sources */,
+				725A60061AEC5F9F00D6342C /* references.c in Sources */,
+				725A5FF61AEC5F9F00D6342C /* houdini_href_e.c in Sources */,
+				FD33B8B12285EBC100BECE98 /* CMImageTextAttachment.m in Sources */,
+				FD6168A82284BD4700517B13 /* CMDocument+AttributedStringAdditions.m in Sources */,
+				729F54291A6864F400CC7448 /* CMDocument.m in Sources */,
+				725116011A69735400337419 /* ONOXMLDocument.m in Sources */,
+				30A07BCD1B5545A50097CEF1 /* render.c in Sources */,
+				06D9F87A1A70465700C00B67 /* CMHTMLUnderlineTransformer.m in Sources */,
+				725A5FFE1AEC5F9F00D6342C /* inlines.c in Sources */,
+				72772BD91A699ACB0059936C /* CMHTMLUtilities.m in Sources */,
+				725A60041AEC5F9F00D6342C /* node.c in Sources */,
+				725115E31A6970A300337419 /* CMStack.m in Sources */,
+				725A5FEE1AEC5F9F00D6342C /* buffer.c in Sources */,
+				72C17A961A6EBA3300212F17 /* CMHTMLRenderer.m in Sources */,
+				725A5FF81AEC5F9F00D6342C /* houdini_html_e.c in Sources */,
+				729F54251A6864F400CC7448 /* CMAttributedStringRenderer.m in Sources */,
+				722E33E91A68E86B004DE919 /* CMTextAttributes.m in Sources */,
+				725A600C1AEC5F9F00D6342C /* xml.c in Sources */,
+				72C17A9C1A6EBD7800212F17 /* CMDocument+HTMLAdditions.m in Sources */,
+				725A60001AEC5F9F00D6342C /* iterator.c in Sources */,
+				725A60081AEC5F9F00D6342C /* scanners.c in Sources */,
+				725A5FFC1AEC5F9F00D6342C /* html.c in Sources */,
+				72772BE91A69ABB20059936C /* CMHTMLStrikethroughTransformer.m in Sources */,
+				725A5FF21AEC5F9F00D6342C /* cmark.c in Sources */,
+				722E33EF1A68EADA004DE919 /* CMCascadingAttributeStack.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		729F54011A68649200CC7448 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				729F54C21A68673800CC7448 /* CMNodeSpec.m in Sources */,
+				729F54C61A68673800CC7448 /* CMParserTestObject.m in Sources */,
+				72C17A9F1A6EBE5600212F17 /* CMHTMLRendererSpec.m in Sources */,
+				729F54C01A68673800CC7448 /* CMIteratorSpec.m in Sources */,
+				729F54C41A68673800CC7448 /* CMParserSpec.m in Sources */,
+				729F54B31A6866C200CC7448 /* DummySpec.swift in Sources */,
+				729F54BE1A68673800CC7448 /* CMDocumentSpec.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		72FE7EC51A68692400F23F46 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				72FE7ED01A68692400F23F46 /* ViewController.swift in Sources */,
+				72FE7ECE1A68692400F23F46 /* AppDelegate.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		72FE7EF01A68695100F23F46 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				72FE7EF91A68695100F23F46 /* AppDelegate.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		720DA5801A68A00300DD05AF /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 729F53FA1A68649200CC7448 /* CocoaMarkdown-Mac */;
+			targetProxy = 720DA57F1A68A00300DD05AF /* PBXContainerItemProxy */;
+		};
+		729F53E81A68641400CC7448 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 729F53D91A68641400CC7448 /* CocoaMarkdown-iOS */;
+			targetProxy = 729F53E71A68641400CC7448 /* PBXContainerItemProxy */;
+		};
+		729F54081A68649200CC7448 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 729F53FA1A68649200CC7448 /* CocoaMarkdown-Mac */;
+			targetProxy = 729F54071A68649200CC7448 /* PBXContainerItemProxy */;
+		};
+		72FE7EEC1A68693200F23F46 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 729F53D91A68641400CC7448 /* CocoaMarkdown-iOS */;
+			targetProxy = 72FE7EEB1A68693200F23F46 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+		72FE7ED11A68692400F23F46 /* Main.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				72FE7ED21A68692400F23F46 /* Base */,
+			);
+			name = Main.storyboard;
+			sourceTree = "<group>";
+		};
+		72FE7ED61A68692400F23F46 /* LaunchScreen.xib */ = {
+			isa = PBXVariantGroup;
+			children = (
+				72FE7ED71A68692400F23F46 /* Base */,
+			);
+			name = LaunchScreen.xib;
+			sourceTree = "<group>";
+		};
+		72FE7EFC1A68695100F23F46 /* MainMenu.xib */ = {
+			isa = PBXVariantGroup;
+			children = (
+				72FE7EFD1A68695100F23F46 /* Base */,
+			);
+			name = MainMenu.xib;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		729F53EE1A68641400CC7448 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				CURRENT_PROJECT_VERSION = 1;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					HAVE_C99_SNPRINTF,
+					"$(inherited)",
+				);
+				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 8.1;
+				ONLY_ACTIVE_ARCH = YES;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+			};
+			name = Debug;
+		};
+		729F53EF1A68641400CC7448 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = YES;
+				CURRENT_PROJECT_VERSION = 1;
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_PREPROCESSOR_DEFINITIONS = HAVE_C99_SNPRINTF;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 8.1;
+				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+			};
+			name = Release;
+		};
+		729F53F11A68641400CC7448 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+				HEADER_SEARCH_PATHS = (
+					"$(inherited)",
+					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+					"$(SDKROOT)/usr/include/libxml2",
+					"$(SRCROOT)/CocoaMarkdown/Configuration",
+				);
+				INFOPLIST_FILE = CocoaMarkdown/Info.plist;
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				IPHONEOS_DEPLOYMENT_TARGET = 9.3;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				OTHER_CFLAGS = "-DCMARK_INLINE=inline";
+				PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = CocoaMarkdown;
+				SDKROOT = iphoneos;
+				SKIP_INSTALL = YES;
+				SWIFT_VERSION = 3.0;
+				USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/External/cmark/src";
+			};
+			name = Debug;
+		};
+		729F53F21A68641400CC7448 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+				HEADER_SEARCH_PATHS = (
+					"$(inherited)",
+					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+					"$(SDKROOT)/usr/include/libxml2",
+					"$(SRCROOT)/CocoaMarkdown/Configuration",
+				);
+				INFOPLIST_FILE = CocoaMarkdown/Info.plist;
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				IPHONEOS_DEPLOYMENT_TARGET = 9.3;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				OTHER_CFLAGS = "-DCMARK_INLINE=inline";
+				PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = CocoaMarkdown;
+				SDKROOT = iphoneos;
+				SKIP_INSTALL = YES;
+				SWIFT_VERSION = 3.0;
+				USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/External/cmark/src";
+			};
+			name = Release;
+		};
+		729F53F41A68641400CC7448 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ENABLE_MODULES = YES;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(DEVELOPER_FRAMEWORKS_DIR)",
+					"$(inherited)",
+				);
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				HEADER_SEARCH_PATHS = (
+					"$(inherited)",
+					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+					"$(SRCROOT)/CocoaMarkdown/Configuration",
+				);
+				INFOPLIST_FILE = CocoaMarkdownTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.3;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = iphoneos;
+				SWIFT_OBJC_BRIDGING_HEADER = "CocoaMarkdownTests/CocoaMarkdownTests-Bridging-Header.h";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 4.2;
+				USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/External/cmark/src";
+			};
+			name = Debug;
+		};
+		729F53F51A68641400CC7448 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ENABLE_MODULES = YES;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(DEVELOPER_FRAMEWORKS_DIR)",
+					"$(inherited)",
+				);
+				HEADER_SEARCH_PATHS = (
+					"$(inherited)",
+					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+					"$(SRCROOT)/CocoaMarkdown/Configuration",
+				);
+				INFOPLIST_FILE = CocoaMarkdownTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.3;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = iphoneos;
+				SWIFT_OBJC_BRIDGING_HEADER = "CocoaMarkdownTests/CocoaMarkdownTests-Bridging-Header.h";
+				SWIFT_VERSION = 4.2;
+				USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/External/cmark/src";
+			};
+			name = Release;
+		};
+		729F540F1A68649200CC7448 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				COMBINE_HIDPI_IMAGES = YES;
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				FRAMEWORK_VERSION = A;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+				HEADER_SEARCH_PATHS = (
+					"$(inherited)",
+					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+					"$(SDKROOT)/usr/include/libxml2",
+					"$(SRCROOT)/CocoaMarkdown/Configuration",
+				);
+				INFOPLIST_FILE = CocoaMarkdown/Info.plist;
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.11;
+				OTHER_CFLAGS = "-DCMARK_INLINE=inline";
+				PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = CocoaMarkdown;
+				SDKROOT = macosx;
+				SKIP_INSTALL = YES;
+				USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/External/cmark/src";
+			};
+			name = Debug;
+		};
+		729F54101A68649200CC7448 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				COMBINE_HIDPI_IMAGES = YES;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				FRAMEWORK_VERSION = A;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
+				HEADER_SEARCH_PATHS = (
+					"$(inherited)",
+					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+					"$(SDKROOT)/usr/include/libxml2",
+					"$(SRCROOT)/CocoaMarkdown/Configuration",
+				);
+				INFOPLIST_FILE = CocoaMarkdown/Info.plist;
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.11;
+				OTHER_CFLAGS = "-DCMARK_INLINE=inline";
+				PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = CocoaMarkdown;
+				SDKROOT = macosx;
+				SKIP_INSTALL = YES;
+				USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/External/cmark/src";
+			};
+			name = Release;
+		};
+		729F54121A68649200CC7448 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ENABLE_MODULES = YES;
+				COMBINE_HIDPI_IMAGES = YES;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(DEVELOPER_FRAMEWORKS_DIR)",
+					"$(inherited)",
+				);
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				HEADER_SEARCH_PATHS = (
+					"$(inherited)",
+					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+					"$(SRCROOT)/CocoaMarkdown/Configuration",
+				);
+				INFOPLIST_FILE = CocoaMarkdownTests/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = macosx;
+				SWIFT_OBJC_BRIDGING_HEADER = "CocoaMarkdownTests/CocoaMarkdownTests-Bridging-Header.h";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 4.2;
+				USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/External/cmark/src";
+			};
+			name = Debug;
+		};
+		729F54131A68649200CC7448 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ENABLE_MODULES = YES;
+				COMBINE_HIDPI_IMAGES = YES;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(DEVELOPER_FRAMEWORKS_DIR)",
+					"$(inherited)",
+				);
+				HEADER_SEARCH_PATHS = (
+					"$(inherited)",
+					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+					"$(SRCROOT)/CocoaMarkdown/Configuration",
+				);
+				INFOPLIST_FILE = CocoaMarkdownTests/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = macosx;
+				SWIFT_OBJC_BRIDGING_HEADER = "CocoaMarkdownTests/CocoaMarkdownTests-Bridging-Header.h";
+				SWIFT_VERSION = 4.2;
+				USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/External/cmark/src";
+			};
+			name = Release;
+		};
+		72FE7EE61A68692400F23F46 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CODE_SIGN_STYLE = Manual;
+				DEVELOPMENT_TEAM = "";
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				INFOPLIST_FILE = "Example-iOS/Info.plist";
+				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = "";
+				SDKROOT = iphoneos;
+				SWIFT_VERSION = 4.2;
+			};
+			name = Debug;
+		};
+		72FE7EE71A68692400F23F46 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CODE_SIGN_STYLE = Manual;
+				DEVELOPMENT_TEAM = "";
+				INFOPLIST_FILE = "Example-iOS/Info.plist";
+				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = "";
+				SDKROOT = iphoneos;
+				SWIFT_VERSION = 4.2;
+			};
+			name = Release;
+		};
+		72FE7F0C1A68695100F23F46 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CODE_SIGN_IDENTITY = "-";
+				COMBINE_HIDPI_IMAGES = YES;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				INFOPLIST_FILE = "Example-Mac/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = macosx;
+				SWIFT_OBJC_BRIDGING_HEADER = "";
+				SWIFT_VERSION = 4.2;
+			};
+			name = Debug;
+		};
+		72FE7F0D1A68695100F23F46 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CODE_SIGN_IDENTITY = "-";
+				COMBINE_HIDPI_IMAGES = YES;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				INFOPLIST_FILE = "Example-Mac/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = macosx;
+				SWIFT_OBJC_BRIDGING_HEADER = "";
+				SWIFT_VERSION = 4.2;
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		729F53D41A68641300CC7448 /* Build configuration list for PBXProject "CocoaMarkdown" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				729F53EE1A68641400CC7448 /* Debug */,
+				729F53EF1A68641400CC7448 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		729F53F01A68641400CC7448 /* Build configuration list for PBXNativeTarget "CocoaMarkdown-iOS" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				729F53F11A68641400CC7448 /* Debug */,
+				729F53F21A68641400CC7448 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		729F53F31A68641400CC7448 /* Build configuration list for PBXNativeTarget "CocoaMarkdownTests-iOS" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				729F53F41A68641400CC7448 /* Debug */,
+				729F53F51A68641400CC7448 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		729F540E1A68649200CC7448 /* Build configuration list for PBXNativeTarget "CocoaMarkdown-Mac" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				729F540F1A68649200CC7448 /* Debug */,
+				729F54101A68649200CC7448 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		729F54111A68649200CC7448 /* Build configuration list for PBXNativeTarget "CocoaMarkdownTests-Mac" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				729F54121A68649200CC7448 /* Debug */,
+				729F54131A68649200CC7448 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		72FE7EE51A68692400F23F46 /* Build configuration list for PBXNativeTarget "Example-iOS" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				72FE7EE61A68692400F23F46 /* Debug */,
+				72FE7EE71A68692400F23F46 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		72FE7F0B1A68695100F23F46 /* Build configuration list for PBXNativeTarget "Example-Mac" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				72FE7F0C1A68695100F23F46 /* Debug */,
+				72FE7F0D1A68695100F23F46 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 729F53D11A68641300CC7448 /* Project object */;
+}

+ 7 - 0
CocoaMarkdown.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:CocoaMarkdown.xcodeproj">
+   </FileRef>
+</Workspace>

+ 37 - 0
CocoaMarkdown.xcodeproj/project.xcworkspace/xcshareddata/CocoaMarkdown.xcscmblueprint

@@ -0,0 +1,37 @@
+{
+  "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "AE7FD09FEC8B285AB86A7422A06B8D893FA72957",
+  "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
+
+  },
+  "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
+    "95438028B10BBB846574013D29F154A00556A9D1" : 0,
+    "D0725CAC6FF2D66F2C83C2C48DC12106D42DAA64" : 0,
+    "AE7FD09FEC8B285AB86A7422A06B8D893FA72957" : 0
+  },
+  "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "2AB53905-D0FC-4290-AFFB-1488CA9A6DB9",
+  "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
+    "95438028B10BBB846574013D29F154A00556A9D1" : "cm\/External\/Nimble\/",
+    "D0725CAC6FF2D66F2C83C2C48DC12106D42DAA64" : "cm\/External\/Quick\/",
+    "AE7FD09FEC8B285AB86A7422A06B8D893FA72957" : "cm\/"
+  },
+  "DVTSourceControlWorkspaceBlueprintNameKey" : "CocoaMarkdown",
+  "DVTSourceControlWorkspaceBlueprintVersion" : 204,
+  "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "CocoaMarkdown.xcodeproj",
+  "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
+    {
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/Quick\/Nimble.git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "95438028B10BBB846574013D29F154A00556A9D1"
+    },
+    {
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/mjstallard\/CocoaMarkdown",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "AE7FD09FEC8B285AB86A7422A06B8D893FA72957"
+    },
+    {
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/Quick\/Quick",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "D0725CAC6FF2D66F2C83C2C48DC12106D42DAA64"
+    }
+  ]
+}

+ 113 - 0
CocoaMarkdown.xcodeproj/xcshareddata/xcschemes/CocoaMarkdown-Mac.xcscheme

@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1010"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "729F53FA1A68649200CC7448"
+               BuildableName = "CocoaMarkdown.framework"
+               BlueprintName = "CocoaMarkdown-Mac"
+               ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "NO"
+            buildForProfiling = "NO"
+            buildForArchiving = "NO"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "729F54041A68649200CC7448"
+               BuildableName = "CocoaMarkdownTests-Mac.xctest"
+               BlueprintName = "CocoaMarkdownTests-Mac"
+               ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "729F54041A68649200CC7448"
+               BuildableName = "CocoaMarkdownTests-Mac.xctest"
+               BlueprintName = "CocoaMarkdownTests-Mac"
+               ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "729F53FA1A68649200CC7448"
+            BuildableName = "CocoaMarkdown.framework"
+            BlueprintName = "CocoaMarkdown-Mac"
+            ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "729F53FA1A68649200CC7448"
+            BuildableName = "CocoaMarkdown.framework"
+            BlueprintName = "CocoaMarkdown-Mac"
+            ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "729F53FA1A68649200CC7448"
+            BuildableName = "CocoaMarkdown.framework"
+            BlueprintName = "CocoaMarkdown-Mac"
+            ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 113 - 0
CocoaMarkdown.xcodeproj/xcshareddata/xcschemes/CocoaMarkdown-iOS.xcscheme

@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1010"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "729F53D91A68641400CC7448"
+               BuildableName = "CocoaMarkdown.framework"
+               BlueprintName = "CocoaMarkdown-iOS"
+               ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "NO"
+            buildForProfiling = "NO"
+            buildForArchiving = "NO"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "729F53E41A68641400CC7448"
+               BuildableName = "CocoaMarkdownTests-iOS.xctest"
+               BlueprintName = "CocoaMarkdownTests-iOS"
+               ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "729F53E41A68641400CC7448"
+               BuildableName = "CocoaMarkdownTests-iOS.xctest"
+               BlueprintName = "CocoaMarkdownTests-iOS"
+               ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "729F53D91A68641400CC7448"
+            BuildableName = "CocoaMarkdown.framework"
+            BlueprintName = "CocoaMarkdown-iOS"
+            ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "729F53D91A68641400CC7448"
+            BuildableName = "CocoaMarkdown.framework"
+            BlueprintName = "CocoaMarkdown-iOS"
+            ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "729F53D91A68641400CC7448"
+            BuildableName = "CocoaMarkdown.framework"
+            BlueprintName = "CocoaMarkdown-iOS"
+            ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 91 - 0
CocoaMarkdown.xcodeproj/xcshareddata/xcschemes/Example-Mac.xcscheme

@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1010"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "72FE7EF31A68695100F23F46"
+               BuildableName = "Example-Mac.app"
+               BlueprintName = "Example-Mac"
+               ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "72FE7EF31A68695100F23F46"
+            BuildableName = "Example-Mac.app"
+            BlueprintName = "Example-Mac"
+            ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "72FE7EF31A68695100F23F46"
+            BuildableName = "Example-Mac.app"
+            BlueprintName = "Example-Mac"
+            ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "72FE7EF31A68695100F23F46"
+            BuildableName = "Example-Mac.app"
+            BlueprintName = "Example-Mac"
+            ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 91 - 0
CocoaMarkdown.xcodeproj/xcshareddata/xcschemes/Example-iOS.xcscheme

@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1010"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "72FE7EC81A68692400F23F46"
+               BuildableName = "Example-iOS.app"
+               BlueprintName = "Example-iOS"
+               ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "72FE7EC81A68692400F23F46"
+            BuildableName = "Example-iOS.app"
+            BlueprintName = "Example-iOS"
+            ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "72FE7EC81A68692400F23F46"
+            BuildableName = "Example-iOS.app"
+            BlueprintName = "Example-iOS"
+            ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "72FE7EC81A68692400F23F46"
+            BuildableName = "Example-iOS.app"
+            BlueprintName = "Example-iOS"
+            ReferencedContainer = "container:CocoaMarkdown.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 25 - 0
CocoaMarkdown/CMAttributeRun.h

@@ -0,0 +1,25 @@
+//
+//  CMAttributeRun.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/15/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "CMPlatformDefines.h"
+
+@class CMStyleAttributes;
+
+@interface CMAttributeRun : NSObject
+@property (nonatomic, readonly) CMStyleAttributes *attributes;
+@property (nonatomic) NSInteger orderedListItemNumber;
+@property (nonatomic, readonly) BOOL listTight;
+
+- (instancetype)initWithAttributes:(CMStyleAttributes *)attributes
+                 orderedListNumber:(NSInteger)orderedListNumber;
+
+@end
+
+CMAttributeRun * CMDefaultAttributeRun(CMStyleAttributes *attributes);
+CMAttributeRun * CMOrderedListAttributeRun(CMStyleAttributes *attributes, NSInteger startingNumber);

+ 33 - 0
CocoaMarkdown/CMAttributeRun.m

@@ -0,0 +1,33 @@
+//
+//  CMAttributeRun.m
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/15/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMAttributeRun.h"
+
+CMAttributeRun * CMDefaultAttributeRun(CMStyleAttributes *attributes)
+{
+    return [[CMAttributeRun alloc] initWithAttributes:attributes orderedListNumber:0];
+}
+
+CMAttributeRun * CMOrderedListAttributeRun(CMStyleAttributes *attributes, NSInteger startingNumber)
+{
+    return [[CMAttributeRun alloc] initWithAttributes:attributes orderedListNumber:startingNumber];
+}
+
+@implementation CMAttributeRun
+
+- (instancetype)initWithAttributes:(CMStyleAttributes *)attributes
+                 orderedListNumber:(NSInteger)orderedListNumber
+{
+    if ((self = [super init])) {
+        _attributes = attributes;
+        _orderedListItemNumber = orderedListNumber;
+    }
+    return self;
+}
+
+@end

+ 47 - 0
CocoaMarkdown/CMAttributedStringRenderer.h

@@ -0,0 +1,47 @@
+//
+//  CMAttributedStringRenderer.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/14/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class CMDocument;
+@class CMTextAttributes;
+@protocol CMHTMLElementTransformer;
+/**
+ *  Renders an attributed string from a Markdown document
+ */
+@interface CMAttributedStringRenderer : NSObject
+
+/**
+ *  Designated initializer.
+ *
+ *  @param document   A Markdown document.
+ *  @param attributes Attributes used to style the string.
+ *
+ *  @return An initialized instance of the receiver.
+ */
+- (instancetype)initWithDocument:(CMDocument *)document attributes:(CMTextAttributes *)attributes;
+
+/**
+ *  Registers a handler to transform HTML elements.
+ *
+ *  Only a single transformer can be registered for an element. If a transformer
+ *  is already registered for an element, it will be replaced.
+ *
+ *  @param transformer The transformer to register.
+ */
+- (void)registerHTMLElementTransformer:(id<CMHTMLElementTransformer>)transformer;
+
+/**
+ *  Renders an attributed string from the Markdown document.
+ *
+ *  @return An attributed string containing the contents of the Markdown document,
+ *  styled using the attributes set on the receiver.
+ */
+- (NSAttributedString *)render;
+
+@end

+ 475 - 0
CocoaMarkdown/CMAttributedStringRenderer.m

@@ -0,0 +1,475 @@
+//
+//  CMAttributedStringRenderer.m
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/14/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMAttributedStringRenderer.h"
+#import "CMAttributeRun.h"
+#import "CMCascadingAttributeStack.h"
+#import "CMStack.h"
+#import "CMHTMLElementTransformer.h"
+#import "CMHTMLElement.h"
+#import "CMHTMLUtilities.h"
+#import "CMTextAttributes.h"
+#import "CMImageTextAttachment.h"
+#import "CMNode.h"
+#import "CMParser.h"
+
+#import "Ono.h"
+
+@interface CMAttributedStringRenderer () <CMParserDelegate>
+@end
+
+@implementation CMAttributedStringRenderer {
+    CMDocument *_document;
+    CMTextAttributes *_attributes;
+    CMCascadingAttributeStack *_attributeStack;
+    CMStack *_HTMLStack;
+    NSMutableDictionary *_tagNameToTransformerMapping;
+    NSMutableAttributedString *_buffer;
+    NSAttributedString *_attributedString;
+}
+
+- (instancetype)initWithDocument:(CMDocument *)document attributes:(CMTextAttributes *)attributes
+{
+    if ((self = [super init])) {
+        _document = document;
+        _attributes = attributes;
+        _tagNameToTransformerMapping = [[NSMutableDictionary alloc] init];
+    }
+    return self;
+}
+
+- (void)registerHTMLElementTransformer:(id<CMHTMLElementTransformer>)transformer
+{
+    NSParameterAssert(transformer);
+    _tagNameToTransformerMapping[[transformer.class tagName]] = transformer;
+}
+
+- (NSAttributedString *)render
+{
+    if (_attributedString == nil) {
+        _attributeStack = [[CMCascadingAttributeStack alloc] init];
+        _HTMLStack = [[CMStack alloc] init];
+        _buffer = [[NSMutableAttributedString alloc] init];
+        
+        CMParser *parser = [[CMParser alloc] initWithDocument:_document delegate:self];
+        [parser parse];
+        
+        _attributedString = [_buffer copy];
+        _attributeStack = nil;
+        _HTMLStack = nil;
+        _buffer = nil;
+    }
+    
+    return _attributedString;
+}
+
+#pragma mark - CMParserDelegate
+
+- (void)parserDidStartDocument:(CMParser *)parser
+{
+    [_attributeStack pushAttributes:_attributes.baseTextAttributes];
+}
+
+- (void)parserDidEndDocument:(CMParser *)parser
+{
+    CFStringTrimWhitespace((__bridge CFMutableStringRef)_buffer.mutableString);
+}
+
+- (void)parser:(CMParser *)parser foundText:(NSString *)text
+{
+    if (! [self isImageDescriptionNode:parser.currentNode]) { // An image description text shall not be append to the current buffer
+        CMHTMLElement *element = [_HTMLStack peek];
+        if (element != nil) {
+            [element.buffer appendString:text];
+        } else {
+            [self appendString:text];
+        }
+    }
+}
+
+- (void)parser:(CMParser *)parser didStartHeaderWithLevel:(NSInteger)level
+{
+    [_attributeStack pushAttributes:[_attributes attributesForHeaderLevel:level]];
+}
+
+- (void)parser:(CMParser *)parser didEndHeaderWithLevel:(NSInteger)level
+{
+    [self closeBlockForNode:parser.currentNode];
+    [_attributeStack pop];
+}
+
+- (void)parserDidStartParagraph:(CMParser *)parser
+{
+    BOOL isInTightList = [self nodeIsInTightMode:parser.currentNode];
+    [_attributeStack pushAttributes:!isInTightList ? _attributes.paragraphAttributes : nil];
+}
+
+- (void)parserDidEndParagraph:(CMParser *)parser
+{
+    [self closeBlockForNode:parser.currentNode];
+    [_attributeStack pop];
+}
+
+- (void)parserDidStartEmphasis:(CMParser *)parser
+{
+    [_attributeStack pushAttributes:_attributes.emphasisAttributes];
+}
+
+- (void)parserDidEndEmphasis:(CMParser *)parser
+{
+    [_attributeStack pop];
+}
+
+- (void)parserDidStartStrong:(CMParser *)parser
+{
+   [_attributeStack pushAttributes:_attributes.strongAttributes];
+}
+
+- (void)parserDidEndStrong:(CMParser *)parse
+{
+    [_attributeStack pop];
+}
+
+- (void)parser:(CMParser *)parser didStartLinkWithURL:(NSURL *)URL title:(NSString *)title
+{
+    CMStyleAttributes * linkStyleAttributes = _attributes.linkAttributes.copy;
+    linkStyleAttributes.stringAttributes [NSLinkAttributeName] = URL;
+#if !TARGET_OS_IPHONE
+    if (title != nil) {
+        linkStyleAttributes.stringAttributes [NSToolTipAttributeName] = title;
+    }
+#endif
+    [_attributeStack pushAttributes:linkStyleAttributes];
+}
+
+- (void)parser:(CMParser *)parser didEndLinkWithURL:(NSURL *)URL title:(NSString *)title
+{
+    [_attributeStack pop];
+}
+
+- (void)parser:(CMParser *)parser didStartImageWithURL:(NSURL *)URL title:(NSString *)title
+{
+    NSTextAttachment* textAttachment = [[CMImageTextAttachment alloc] initWithImageURL:URL];
+    if (textAttachment != nil) {
+        // Detect if an image has its own paragraph, in which cas we can apply specific attributes.
+        // (Note: This test also detect the case: image in link in paragraph)
+        CMNode* imageNode = parser.currentNode;
+        BOOL isInImageParagraph = ((imageNode.next == nil) && (imageNode.previous == nil) 
+                                   && ((imageNode.parent.type == CMNodeTypeParagraph) 
+                                       || ((imageNode.parent.next == nil) && (imageNode.parent.previous == nil) && (imageNode.parent.parent.type == CMNodeTypeParagraph))));
+        
+        CMHTMLElement *element = [_HTMLStack peek];
+        if (element != nil) {
+            // TODO: how should we handle a markdown image inside HTML;? Is this possible????
+        } else {
+            CMStyleAttributes * imageAttachmentAttributes;
+            if (isInImageParagraph) {
+                imageAttachmentAttributes = _attributes.imageParagraphAttributes.copy;
+            }
+            else {
+                imageAttachmentAttributes = [CMStyleAttributes new];
+            }
+            imageAttachmentAttributes.stringAttributes[NSAttachmentAttributeName] = textAttachment;
+#if !TARGET_OS_IPHONE
+            CMNode *imageDescriptionNode = imageNode.firstChild;
+            if ((imageDescriptionNode.type == CMNodeTypeText) && (imageDescriptionNode.stringValue.length > 0)) {
+                imageAttachmentAttributes.stringAttributes [NSToolTipAttributeName] = imageDescriptionNode.stringValue;
+            }
+#endif     
+            [_attributeStack pushAttributes:imageAttachmentAttributes];
+            
+            const unichar attachmentChar = NSAttachmentCharacter;
+            [self appendString:[NSString stringWithCharacters:&attachmentChar length:1]];
+            
+            if (isInImageParagraph) {
+                [self closeBlockForNode:imageNode];
+            }
+        }
+    }
+}
+
+- (void)parser:(CMParser *)parser didEndImageWithURL:(NSURL *)URL title:(NSString *)title
+{
+    [_attributeStack pop];
+}
+
+- (void)parser:(CMParser *)parser foundHTML:(NSString *)HTML
+{
+    NSString *tagName = CMTagNameFromHTMLTag(HTML);
+    if (tagName.length != 0) {
+        CMHTMLElement *element = [self newHTMLElementForTagName:tagName HTML:HTML];
+        if (element != nil) {
+            [self appendHTMLElement:element];
+        }
+    }
+}
+
+- (void)parser:(CMParser *)parser foundInlineHTML:(NSString *)HTML
+{
+    NSString *tagName = CMTagNameFromHTMLTag(HTML);
+    if (tagName.length != 0) {
+        CMHTMLElement *element = nil;
+        if (CMIsHTMLVoidTagName(tagName)) {
+            element = [self newHTMLElementForTagName:tagName HTML:HTML];
+            if (element != nil) {
+                [self appendHTMLElement:element];
+            }
+        } else if (CMIsHTMLClosingTag(HTML)) {
+            if ((element = [_HTMLStack pop])) {
+                NSAssert([element.tagName isEqualToString:tagName], @"Closing tag does not match opening tag");
+                [element.buffer appendString:HTML];
+                [self appendHTMLElement:element];
+            }
+        } else if (CMIsHTMLTag(HTML)) {
+            element = [self newHTMLElementForTagName:tagName HTML:HTML];
+            if (element != nil) {
+                [_HTMLStack push:element];
+            }
+        }
+    }
+}
+
+- (void)parser:(CMParser *)parser foundCodeBlock:(NSString *)code info:(NSString *)info
+{
+    [_attributeStack pushAttributes:_attributes.codeBlockAttributes];
+    if ([code hasSuffix:@"\n"]) {
+        code = [code substringToIndex:code.length - 1]; // Remove final "\n"
+    }
+    NSString* const lineSeparatorCharacterString = @"\u2028";
+    [self appendString:[code stringByReplacingOccurrencesOfString:@"\n" withString:lineSeparatorCharacterString]];
+    [self closeBlockForNode:parser.currentNode];
+    [_attributeStack pop];
+}
+
+- (void)parser:(CMParser *)parser foundInlineCode:(NSString *)code
+{
+    [_attributeStack pushAttributes:_attributes.inlineCodeAttributes];
+    [self appendString:code];
+    [_attributeStack pop];
+}
+
+- (void)parserFoundSoftBreak:(CMParser *)parser
+{
+    [self appendString:@" "];
+}
+
+- (void)parserFoundLineBreak:(CMParser *)parser
+{
+    [self appendString:@"\u2028"];
+}
+
+- (void)parserDidStartBlockQuote:(CMParser *)parser
+{
+    [_attributeStack pushAttributes:_attributes.blockQuoteAttributes];
+}
+
+- (void)parserDidEndBlockQuote:(CMParser *)parser
+{
+    [self closeBlockForNode:parser.currentNode];
+    [_attributeStack pop];
+}
+
+- (void)parser:(CMParser *)parser didStartUnorderedListWithTightness:(BOOL)tight
+{
+    if ([self sublistLevel:parser.currentNode.parent] == 0) {
+       [_attributeStack pushAttributes:_attributes.unorderedListAttributes];
+    }
+    else {
+        [self closeBlockForNode:parser.currentNode]; // When starting a sublist, the parent item must have its block closed first
+        [_attributeStack pushAttributes:_attributes.unorderedSublistAttributes];
+    }
+}
+
+- (void)parser:(CMParser *)parser didEndUnorderedListWithTightness:(BOOL)tight
+{
+    [_attributeStack pop];
+}
+
+- (void)parser:(CMParser *)parser didStartOrderedListWithStartingNumber:(NSInteger)num tight:(BOOL)tight
+{
+    if ([self sublistLevel:parser.currentNode.parent] == 0) {
+        [_attributeStack pushOrderedListAttributes:_attributes.orderedListAttributes withStartingNumber:num];
+    }
+    else {
+        [self closeBlockForNode:parser.currentNode]; // When starting a sublist, the parent item must have its block closed first
+        [_attributeStack pushOrderedListAttributes:_attributes.orderedSublistAttributes withStartingNumber:num];
+    }
+}
+
+- (void)parser:(CMParser *)parser didEndOrderedListWithStartingNumber:(NSInteger)num tight:(BOOL)tight
+{
+    [_attributeStack pop];
+}
+
+- (void)parserDidStartListItem:(CMParser *)parser
+{
+    CMAttributeRun *parentRun = [_attributeStack peek];
+    CMStyleAttributes * parentAttributes = parentRun.attributes;
+    
+    CMNode *node = parser.currentNode.parent;
+    switch (node.listType) {
+        case CMListTypeNone:
+            NSAssert(NO, @"Parent node of list item must be a list");
+            break;
+        case CMListTypeUnordered: {
+            [self appendString:[NSString stringWithFormat:@"%@\t", parentAttributes.paragraphStyleAttributes [CMParagraphStyleAttributeListItemBulletString]]];
+            [_attributeStack pushAttributes:_attributes.unorderedListItemAttributes];
+            break;
+        }
+        case CMListTypeOrdered: {
+            [self appendString:[[NSString stringWithFormat:parentAttributes.paragraphStyleAttributes[CMParagraphStyleAttributeListItemNumberFormat], 
+                                 (long)parentRun.orderedListItemNumber] 
+                                stringByAppendingString:@"\t"]];
+            parentRun.orderedListItemNumber++;
+            [_attributeStack pushAttributes:_attributes.orderedListItemAttributes];
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+- (void)parserDidEndListItem:(CMParser *)parser
+{
+    [self closeBlockForNode:parser.currentNode];
+    [_attributeStack pop];
+}
+
+#pragma mark - Private
+
+- (NSUInteger)sublistLevel:(CMNode *)node
+{
+    if (node.parent == nil) {
+        return 0;
+    } else {
+        return (node.listType == CMListTypeNone ? 0 : 1) + [self sublistLevel:node.parent];
+    }
+}
+
+- (CMHTMLElement *)newHTMLElementForTagName:(NSString *)tagName HTML:(NSString *)HTML
+{
+    NSParameterAssert(tagName);
+    id<CMHTMLElementTransformer> transformer = _tagNameToTransformerMapping[tagName];
+    if (transformer != nil) {
+        CMHTMLElement *element = [[CMHTMLElement alloc] initWithTransformer:transformer];
+        [element.buffer appendString:HTML];
+        return element;
+    }
+    return nil;
+}
+
+- (BOOL)isImageDescriptionNode:(CMNode *)node
+{
+    return node.parent.type == CMNodeTypeImage;
+}
+
+- (BOOL)nodeIsInTightMode:(CMNode *)node
+{
+    CMNode *grandparent = node.parent.parent;
+    return grandparent.listTight;
+}
+
+- (void)appendString:(NSString *)string
+{
+    NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:string attributes:_attributeStack.cascadedAttributes];
+    [_buffer appendAttributedString:attrString];
+}
+
+- (void)closeBlockForNode:(CMNode *)currentNode
+{
+    // Add a paragraph boundary to the attributted string if needed
+    if (![_buffer.string hasSuffix:@"\n"]) {
+        
+        NSRange bufferLastParagraphRange = [_buffer.string paragraphRangeForRange:NSMakeRange(_buffer.string.length, 0)];
+        
+        [self appendString:@"\n"];
+        
+        if (bufferLastParagraphRange.length > 0) {
+            // Extend the current paragraph style to enclode the whole paragraph if needed
+            NSParagraphStyle* bufferLastParagraphStyle = [_buffer attribute:NSParagraphStyleAttributeName atIndex:bufferLastParagraphRange.location effectiveRange:NULL];
+            NSParagraphStyle* appendedParagraphStyle = _attributeStack.cascadedAttributes[NSParagraphStyleAttributeName];
+            if ((appendedParagraphStyle != nil) && ![appendedParagraphStyle isEqual:bufferLastParagraphStyle]) {
+                [_buffer addAttribute:NSParagraphStyleAttributeName value:appendedParagraphStyle range:bufferLastParagraphRange];
+            }
+            
+            if ([self sublistLevel:currentNode] != 0) {
+                // In a list: adjust the indentation and tabs of list item's first paragraphs
+                [self adjustListItemIndentForNode:currentNode paragraphStyle:appendedParagraphStyle inRange:bufferLastParagraphRange];
+            }
+        }
+    }
+}
+
+static NSTextTab * textTabWithPosition(CGFloat tabPosition)
+{
+    return [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentNatural location:tabPosition options:@{}];
+}
+
+- (void) adjustListItemIndentForNode:(CMNode*)currentNode paragraphStyle:(NSParagraphStyle*)currentParagraphStyle inRange:(NSRange)currentParagraphRange
+{
+    NSUInteger listAttributesStackDepth = 0;
+    CGFloat itemContentIndent = 0;
+    
+    while ((currentNode != nil) && (currentNode.type != CMNodeTypeItem)) {
+        if ([currentNode isEqual:currentNode.parent.firstChild]) {
+            // indentation change is only needed for list-items' first child
+            
+            CMStyleAttributes* currentStyleAttributes = [_attributeStack attributesWithDepth:listAttributesStackDepth];
+            NSNumber* currentFirstLineExtraIndent = currentStyleAttributes.paragraphStyleAttributes[CMParagraphStyleAttributeFirstLineHeadExtraIndent];
+            itemContentIndent += currentFirstLineExtraIndent.doubleValue;
+            
+            currentNode = currentNode.parent;
+            listAttributesStackDepth += 1;
+        }
+        else {
+            currentNode = nil;
+        }
+    }
+    
+    if (currentNode.parent.type == CMNodeTypeList) {
+        CMStyleAttributes* listStyleAttributes = [_attributeStack attributesWithDepth:listAttributesStackDepth + 1];
+        
+        NSNumber* listItemMarkerIndent = listStyleAttributes.paragraphStyleAttributes [CMParagraphStyleAttributeListItemLabelIndent];
+        if ([listItemMarkerIndent isKindOfClass:[NSNumber class]]) {
+            NSMutableParagraphStyle* itemParagrahStyle = [currentParagraphStyle mutableCopy];
+            // Set a tab at the original first line head indent, plus a few extra tabs in case the list item marker extends beyond the first tab
+            // (typically with dynamic type, in case of bigger text size, extra tabs will help a list item to stay on a single line)
+            itemParagrahStyle.tabStops = @[ textTabWithPosition(itemParagrahStyle.firstLineHeadIndent),
+                                            textTabWithPosition(itemParagrahStyle.firstLineHeadIndent + listItemMarkerIndent.doubleValue),
+                                            textTabWithPosition(itemParagrahStyle.firstLineHeadIndent + listItemMarkerIndent.doubleValue * 2)];
+            itemParagrahStyle.firstLineHeadIndent -= listItemMarkerIndent.doubleValue + itemContentIndent;
+            // And update the pargraph style attributes
+            [_buffer addAttribute:NSParagraphStyleAttributeName value:itemParagrahStyle range:currentParagraphRange];
+        }
+    }
+}    
+
+- (void)appendHTMLElement:(CMHTMLElement *)element
+{
+    NSError *error = nil;
+    ONOXMLDocument *document = [ONOXMLDocument HTMLDocumentWithString:element.buffer encoding:NSUTF8StringEncoding error:&error];
+    if (document == nil) {
+        NSLog(@"Error creating HTML document for buffer \"%@\": %@", element.buffer, error);
+        return;
+    }
+    
+    ONOXMLElement *XMLElement = document.rootElement[0][0];
+    NSDictionary *attributes = _attributeStack.cascadedAttributes;
+    NSAttributedString *attrString = [element.transformer attributedStringForElement:XMLElement attributes:attributes];
+    
+    if (attrString != nil) {
+        CMHTMLElement *parentElement = [_HTMLStack peek];
+        if (parentElement == nil) {
+            [_buffer appendAttributedString:attrString];
+        } else {
+            [parentElement.buffer appendString:attrString.string];
+        }
+    }
+}
+
+@end

+ 24 - 0
CocoaMarkdown/CMCascadingAttributeStack.h

@@ -0,0 +1,24 @@
+//
+//  CMCascadingAttributeStack.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/15/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class CMStyleAttributes;
+@class CMAttributeRun;
+
+@interface CMCascadingAttributeStack : NSObject
+
+@property (nonatomic, readonly) NSDictionary *cascadedAttributes;
+
+- (void) pushAttributes:(CMStyleAttributes*)attributes;
+- (void) pushOrderedListAttributes:(CMStyleAttributes*)attributes withStartingNumber:(NSInteger)startingNumber;
+- (void)pop;
+- (CMAttributeRun *)peek;
+
+- (CMStyleAttributes*) attributesWithDepth:(NSUInteger)depth; // depth=0 means stack top
+@end

+ 319 - 0
CocoaMarkdown/CMCascadingAttributeStack.m

@@ -0,0 +1,319 @@
+//
+//  CMCascadingAttributeStack.m
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/15/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMCascadingAttributeStack.h"
+#import "CMAttributeRun.h"
+#import "CMPlatformDefines.h"
+#import "CMStack.h"
+#import "CMTextAttributes.h"
+
+@interface CMFont (CMAdditions)
+- (CMFont*) fontByAddingCMAttributes:(NSDictionary<CMFontDescriptorAttributeName, id>*)addedFontAttributes;
+@end
+
+@interface NSParagraphStyle (CMAdditions)
++ (NSParagraphStyle*) paragraphStyleWithCMAttributes:(NSDictionary<CMParagraphStyleAttributeName, id> *)paragraphStyleAttributes;
+- (NSParagraphStyle*) paragraphStyleByAddingCMAttributes:(NSDictionary<CMParagraphStyleAttributeName, id> *)paragraphStyleAttributes;
+@end
+
+
+@implementation CMCascadingAttributeStack {
+    CMStack<CMAttributeRun*> *_stack;
+    NSMutableArray<NSDictionary*> * _cascadedAttributes;
+}
+
+- (instancetype)init
+{
+    if ((self = [super init])) {
+        _stack = [[CMStack alloc] init];
+        _cascadedAttributes = [NSMutableArray new];
+    }
+    return self;
+}
+
+- (void) pushAttributes:(CMStyleAttributes*)attributes
+{
+    [self push:CMDefaultAttributeRun(attributes)];
+}
+
+- (void) pushOrderedListAttributes:(CMStyleAttributes*)attributes withStartingNumber:(NSInteger)startingNumber
+{
+    [self push:CMOrderedListAttributeRun(attributes, startingNumber)];
+}
+
+- (CMStyleAttributes*) attributesWithDepth:(NSUInteger)depth
+{
+    return (depth < _stack.objects.count) ? _stack.objects[_stack.objects.count - 1 - depth].attributes : nil;
+}
+
+- (void)push:(CMAttributeRun *)run
+{
+    [_stack push:run];
+}
+
+- (void)pop
+{
+    [_stack pop];
+    if (_cascadedAttributes.count > _stack.objects.count) {
+        [_cascadedAttributes removeLastObject];
+    }
+}
+
+- (CMAttributeRun *)peek
+{
+    return [_stack peek];
+}
+
+- (NSDictionary *)cascadedAttributes
+{
+    if (_cascadedAttributes.count < _stack.objects.count) {
+        
+        NSMutableDictionary *combinedAttributes = [NSMutableDictionary dictionaryWithDictionary:_cascadedAttributes.lastObject ?: @{}];
+        
+        for (NSUInteger level = _cascadedAttributes.count; level < _stack.objects.count; level += 1) {
+
+            CMStyleAttributes * currentStyleAttributes = _stack.objects[level].attributes;
+            
+            if (currentStyleAttributes != nil) {
+                
+                // Set explicit string attributes
+                [combinedAttributes addEntriesFromDictionary:currentStyleAttributes.stringAttributes];
+                
+                // Set font attributes
+                if (currentStyleAttributes.fontAttributes.count > 0) {
+                    CMFont *baseFont = combinedAttributes[NSFontAttributeName];
+                    CMFont *adjustedFont = nil;
+                    if (baseFont != nil) {
+                        adjustedFont = [baseFont fontByAddingCMAttributes:currentStyleAttributes.fontAttributes];
+                    }
+                    else {
+                        CMFontDescriptor * adjustedFontDescriptor = [CMFontDescriptor fontDescriptorWithFontAttributes:currentStyleAttributes.fontAttributes];
+                        if (adjustedFontDescriptor != nil) {
+                            adjustedFont = [CMFont fontWithDescriptor:adjustedFontDescriptor size:adjustedFontDescriptor.pointSize];
+                        }
+                    }
+                    if (adjustedFont != nil) {
+                        combinedAttributes[NSFontAttributeName] = adjustedFont;
+                    }
+                }
+                
+                // Set paragraph style attributes
+                if (currentStyleAttributes.paragraphStyleAttributes.count > 0) {
+                    NSParagraphStyle* baseParagraphStyle = combinedAttributes[NSParagraphStyleAttributeName];
+                    NSParagraphStyle* adjustedParagraphStyle = nil;
+                    if (baseParagraphStyle != nil) {
+                        adjustedParagraphStyle = [baseParagraphStyle paragraphStyleByAddingCMAttributes:currentStyleAttributes.paragraphStyleAttributes];
+                    }
+                    else {
+                        adjustedParagraphStyle = [NSParagraphStyle paragraphStyleWithCMAttributes:currentStyleAttributes.paragraphStyleAttributes];
+                    }
+                    if (adjustedParagraphStyle != nil) {
+                        combinedAttributes[NSParagraphStyleAttributeName] = adjustedParagraphStyle;
+                    }
+                }
+            }
+            
+            [_cascadedAttributes addObject:combinedAttributes.copy];
+        }
+    }
+    return _cascadedAttributes.lastObject;
+}
+
+@end
+
+
+@implementation CMFont (CMAdditions)
+
+#if TARGET_OS_IPHONE
+#define CMFontNameAttribute UIFontDescriptorNameAttribute
+#define CMFontFamilyAttribute UIFontDescriptorFamilyAttribute
+#define CMFontTraitsAttribute UIFontDescriptorTraitsAttribute
+#define CMFontDescriptorTraitKey UIFontDescriptorTraitKey
+#define CMFontSymbolicTraitKey UIFontSymbolicTrait
+
+#else
+#define CMFontNameAttribute NSFontNameAttribute
+#define CMFontFamilyAttribute NSFontFamilyAttribute
+#define CMFontTraitsAttribute NSFontTraitsAttribute
+#define CMFontDescriptorTraitKey NSFontDescriptorTraitKey
+#define CMFontSymbolicTraitKey NSFontSymbolicTrait
+#endif
+
+
+- (CMFont*) fontByAddingCMAttributes:(NSDictionary<CMFontDescriptorAttributeName, id>*)addedFontAttributes
+{
+    CMFont* matchingFont = nil;
+    CMFontDescriptor* currentFontDescriptor = self.fontDescriptor;
+    
+#if TARGET_OS_IPHONE
+    // Get the current font text style
+    UIFontTextStyle currentFontTextStyle = [currentFontDescriptor objectForKey:UIFontDescriptorTextStyleAttribute] ?: UIFontTextStyleBody;
+#endif
+    
+    if ([addedFontAttributes isKindOfClass:[NSDictionary class]] && (addedFontAttributes.count > 0)) {
+        NSMutableDictionary<CMFontDescriptorAttributeName, id>* fontAttributes = [currentFontDescriptor.fontAttributes mutableCopy];
+        
+        if (((addedFontAttributes[CMFontTraitsAttribute] != nil) || (addedFontAttributes[CMFontFamilyAttribute] != nil)) 
+            && (addedFontAttributes[CMFontNameAttribute] == nil)) {
+            // Replace the font name in the font attributes by font-family
+            fontAttributes[CMFontFamilyAttribute] = [currentFontDescriptor objectForKey:CMFontFamilyAttribute];
+            [fontAttributes removeObjectForKey:CMFontNameAttribute];
+        }
+        
+        if ((addedFontAttributes[CMFontFamilyAttribute] != nil) || (addedFontAttributes[CMFontNameAttribute] != nil)) {
+            // A font is specified in the added attributes: Remove an eventual text-style font attribute as it could break the font change
+#if TARGET_OS_IPHONE
+            [fontAttributes removeObjectForKey:UIFontDescriptorTextStyleAttribute];
+#else
+            [fontAttributes removeObjectForKey:@"NSCTFontUIUsageAttribute"];
+#endif
+        }
+        
+        // Add the parameter attributes
+        [fontAttributes addEntriesFromDictionary:addedFontAttributes];
+        
+        // If only font traits are added, try to preserve current font traits
+        BOOL hasCombinedFontTraits = NO;
+        NSDictionary<CMFontDescriptorTraitKey, id>* addedFontTraits = addedFontAttributes[CMFontTraitsAttribute];
+        NSDictionary<CMFontDescriptorTraitKey, id>* currentFontTraits;
+        if ((addedFontTraits != nil) && (addedFontAttributes.count == 1)) {
+            currentFontTraits = [currentFontDescriptor objectForKey:CMFontTraitsAttribute];
+            NSUInteger currentSymbolicTraits = [currentFontTraits[CMFontSymbolicTraitKey] unsignedIntValue];
+            NSUInteger addedSymbolicTraits = [addedFontTraits[CMFontSymbolicTraitKey] unsignedIntValue];
+            
+            NSMutableDictionary<CMFontDescriptorTraitKey, id>* combinedFontTraits = [NSMutableDictionary new];
+            [currentFontTraits enumerateKeysAndObjectsUsingBlock:^(CMFontDescriptorTraitKey traitKey, id traitValue, BOOL * _Nonnull stop) {
+                if (![traitKey isEqualToString:CMFontSymbolicTraitKey] && ([traitValue doubleValue] != 0)) {
+                    // Only keep traits with non-default value
+                    combinedFontTraits[traitKey] = traitValue;
+                }
+            }];
+            [combinedFontTraits addEntriesFromDictionary:addedFontAttributes[CMFontTraitsAttribute]];
+            // Combine symbolic traits
+            if ((currentSymbolicTraits != 0) && (addedSymbolicTraits != 0)) {
+                combinedFontTraits[CMFontSymbolicTraitKey] = @(currentSymbolicTraits | addedSymbolicTraits);
+            }
+            fontAttributes[CMFontTraitsAttribute] = combinedFontTraits;
+            hasCombinedFontTraits = YES;
+        }
+        
+        // Get a font descriptor with the transformed font attributes
+        CMFontDescriptor* matchingFontDescriptor = [CMFontDescriptor fontDescriptorWithFontAttributes:fontAttributes];
+        
+        if (hasCombinedFontTraits && (matchingFontDescriptor != nil)) {
+            // Check if the font descriptor consistently creates a font with the expected font family
+            NSString* expectedFontFamily = [matchingFontDescriptor objectForKey:CMFontFamilyAttribute];
+            NSString* resultingFontFamily = [[CMFont fontWithDescriptor:matchingFontDescriptor size:0].fontDescriptor objectForKey:CMFontFamilyAttribute];
+            if (! [resultingFontFamily isEqualToString:expectedFontFamily]) {
+                // Retry without font traits attribute inheritance
+                fontAttributes [CMFontTraitsAttribute] = addedFontTraits;
+                matchingFontDescriptor = [CMFontDescriptor fontDescriptorWithFontAttributes:fontAttributes];
+                
+                // If added font traits break the expected font familly, ignore them
+                resultingFontFamily = [[CMFont fontWithDescriptor:matchingFontDescriptor size:0].fontDescriptor objectForKey:CMFontFamilyAttribute];
+                if (! [resultingFontFamily isEqualToString:expectedFontFamily]) {
+                    // Restore current font traits
+                    fontAttributes [CMFontTraitsAttribute] = currentFontTraits;
+                    matchingFontDescriptor = [CMFontDescriptor fontDescriptorWithFontAttributes:fontAttributes];
+                }
+            }            
+        }
+        
+        if (matchingFontDescriptor != nil) {
+    
+#if TARGET_OS_IPHONE
+            if (@available(iOS 11.0, *)) {
+                CGFloat currentStyleFontSize = [UIFont preferredFontForTextStyle:currentFontTextStyle].pointSize;
+                CGFloat currentStyleBaseFontSize = [UIFont preferredFontForTextStyle:currentFontTextStyle 
+                                                compatibleWithTraitCollection:[UITraitCollection traitCollectionWithPreferredContentSizeCategory:UIContentSizeCategoryMedium]].pointSize;
+                UIFont* nonScalableFont = [UIFont fontWithDescriptor:matchingFontDescriptor 
+                                                                size:matchingFontDescriptor.pointSize * currentStyleBaseFontSize / currentStyleFontSize];
+                matchingFont = [[UIFontMetrics metricsForTextStyle:currentFontTextStyle] scaledFontForFont:nonScalableFont];
+            }
+            else {
+                matchingFont = [UIFont fontWithDescriptor:matchingFontDescriptor size:matchingFontDescriptor.pointSize];
+            }
+#else   
+            matchingFont = [CMFont fontWithDescriptor:matchingFontDescriptor size:matchingFontDescriptor.pointSize];
+#endif
+
+        }
+    }
+
+    return matchingFont;
+}
+
+@end
+
+
+#pragma mark - NSParagraphStyle additions
+
+@interface NSMutableParagraphStyle (CMAdditions)
+- (void) applyParagraphStyleAttributes:(NSDictionary<CMParagraphStyleAttributeName, id> *)paragraphStyleAttributes;
+@end
+
+@implementation NSParagraphStyle (CMAdditions)
+
++ (NSParagraphStyle*) paragraphStyleWithCMAttributes:(NSDictionary<CMParagraphStyleAttributeName, id> *)paragraphStyleAttributes
+{
+    NSMutableParagraphStyle* newParagraphStyle = nil;
+    if ([paragraphStyleAttributes isKindOfClass:[NSDictionary class]] && (paragraphStyleAttributes.count > 0)) {
+        newParagraphStyle = [NSMutableParagraphStyle new];
+        [newParagraphStyle applyParagraphStyleAttributes:paragraphStyleAttributes];
+    }
+    return newParagraphStyle;
+}
+
+- (NSParagraphStyle*) paragraphStyleByAddingCMAttributes:(NSDictionary<CMParagraphStyleAttributeName, id> *)paragraphStyleAttributes
+{
+    NSMutableParagraphStyle* newParagraphStyle = nil;
+    if ([paragraphStyleAttributes isKindOfClass:[NSDictionary class]] && (paragraphStyleAttributes.count > 0)) {
+        newParagraphStyle = [self mutableCopy];
+        [newParagraphStyle applyParagraphStyleAttributes:paragraphStyleAttributes];
+    }
+    return newParagraphStyle;
+}
+
+@end
+
+@implementation NSMutableParagraphStyle (CMAdditions)
+
+- (void) applyParagraphStyleAttributes:(NSDictionary<CMParagraphStyleAttributeName, id> *)paragraphStyleAttributes
+{
+    static NSDictionary* applyAttributeBlocks;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        applyAttributeBlocks = 
+        @{ CMParagraphStyleAttributeLineSpacing: ^(NSMutableParagraphStyle* paragraphStyle, id attribute){ paragraphStyle.lineSpacing = [attribute doubleValue]; },
+           CMParagraphStyleAttributeParagraphSpacing: ^(NSMutableParagraphStyle* paragraphStyle, id attribute){ paragraphStyle.paragraphSpacing = [attribute doubleValue]; },
+           CMParagraphStyleAttributeAlignment: ^(NSMutableParagraphStyle* paragraphStyle, id attribute){ paragraphStyle.alignment = (NSTextAlignment)[attribute integerValue]; },
+           CMParagraphStyleAttributeLineBreakMode: ^(NSMutableParagraphStyle* paragraphStyle, id attribute){ paragraphStyle.lineBreakMode = (NSLineBreakMode)[attribute integerValue]; },
+           CMParagraphStyleAttributeMinimumLineHeight: ^(NSMutableParagraphStyle* paragraphStyle, id attribute){ paragraphStyle.minimumLineHeight = [attribute doubleValue]; },
+           CMParagraphStyleAttributeMaximumLineHeight: ^(NSMutableParagraphStyle* paragraphStyle, id attribute){ paragraphStyle.maximumLineHeight = [attribute doubleValue]; },
+           CMParagraphStyleAttributeLineHeightMultiple: ^(NSMutableParagraphStyle* paragraphStyle, id attribute){ paragraphStyle.lineHeightMultiple = [attribute doubleValue]; },
+           CMParagraphStyleAttributeParagraphSpacingBefore: ^(NSMutableParagraphStyle* paragraphStyle, id attribute){ paragraphStyle.paragraphSpacingBefore = [attribute doubleValue]; },
+           CMParagraphStyleAttributeHyphenationFactor: ^(NSMutableParagraphStyle* paragraphStyle, id attribute){ paragraphStyle.hyphenationFactor = [attribute floatValue]; },
+           // Attributes with defining increment values
+           CMParagraphStyleAttributeFirstLineHeadExtraIndent: ^(NSMutableParagraphStyle* paragraphStyle, id attribute){ paragraphStyle.firstLineHeadIndent += [attribute doubleValue]; },
+           CMParagraphStyleAttributeHeadExtraIndent: ^(NSMutableParagraphStyle* paragraphStyle, id attribute){ paragraphStyle.headIndent += [attribute doubleValue]; },
+           CMParagraphStyleAttributeTailExtraIndent: ^(NSMutableParagraphStyle* paragraphStyle, id attribute){ paragraphStyle.tailIndent += [attribute doubleValue]; },
+           
+           // List-specific attributes (ignored)
+           CMParagraphStyleAttributeListItemLabelIndent: ^(NSMutableParagraphStyle* paragraphStyle, id attribute){},
+           CMParagraphStyleAttributeListItemBulletString: ^(NSMutableParagraphStyle* paragraphStyle, id attribute){},
+           CMParagraphStyleAttributeListItemNumberFormat: ^(NSMutableParagraphStyle* paragraphStyle, id attribute){},
+        };
+    });
+    
+    [paragraphStyleAttributes enumerateKeysAndObjectsUsingBlock:^(CMParagraphStyleAttributeName attributeName, id attributeValue, BOOL * _Nonnull stop) {
+        
+        ((void(^)(NSMutableParagraphStyle* paragraphStyle, id attribute))applyAttributeBlocks[attributeName])(self, attributeValue);
+    }];
+}
+
+@end

+ 25 - 0
CocoaMarkdown/CMDocument+AttributedStringAdditions.h

@@ -0,0 +1,25 @@
+//
+//  CMDocument+AttributedStringAdditions.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/20/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMDocument.h"
+
+@class CMTextAttributes;
+
+@interface CMDocument (AttributedStringAdditions)
+
+/**
+ *  Creates an attributed string from the receiver that is styled using the
+ *  specified attributes.
+ *
+ *  @param attributes Attributes used to style the text.
+ *
+ *  @return Attributed string containing the formatted contents of the receiver.
+ */
+- (NSAttributedString *)attributedStringWithAttributes:(CMTextAttributes *)attributes;
+
+@end

+ 20 - 0
CocoaMarkdown/CMDocument+AttributedStringAdditions.m

@@ -0,0 +1,20 @@
+//
+//  CMDocument+AttributedStringAdditions.m
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/20/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMDocument+AttributedStringAdditions.h"
+#import "CMAttributedStringRenderer.h"
+
+@implementation CMDocument (AttributedStringAdditions)
+
+- (NSAttributedString *)attributedStringWithAttributes:(CMTextAttributes *)attributes
+{
+    CMAttributedStringRenderer *renderer = [[CMAttributedStringRenderer alloc] initWithDocument:self attributes:attributes];
+    return [renderer render];
+}
+
+@end

+ 21 - 0
CocoaMarkdown/CMDocument+HTMLAdditions.h

@@ -0,0 +1,21 @@
+//
+//  CMDocument+HTMLAdditions.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/20/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMDocument.h"
+#import "CMHTMLRenderer.h"
+
+@interface CMDocument (HTMLAdditions)
+
+/**
+ *  Creates an HTML representation of the receiver.
+ *
+ *  @return String containing the HTML representation of the receiver.
+ */
+- (NSString *)HTMLString;
+
+@end

+ 19 - 0
CocoaMarkdown/CMDocument+HTMLAdditions.m

@@ -0,0 +1,19 @@
+//
+//  CMDocument+HTMLAdditions.m
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/20/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMDocument+HTMLAdditions.h"
+
+@implementation CMDocument (HTMLAdditions)
+
+- (NSString *)HTMLString
+{
+    CMHTMLRenderer *renderer = [[CMHTMLRenderer alloc] initWithDocument:self];
+    return [renderer render];
+}
+
+@end

+ 92 - 0
CocoaMarkdown/CMDocument.h

@@ -0,0 +1,92 @@
+//
+//  CMDocument.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/12/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class CMNode;
+
+typedef NS_OPTIONS(NSInteger, CMDocumentOptions) {
+    /** 
+     * Include a `data-sourcepos` attribute on all block elements.
+     */
+    CMDocumentOptionsSourcepos = (1 << 0),
+    /** 
+     * Render `softbreak` elements as hard line breaks.
+     */
+    CMDocumentOptionsHardBreaks = (1 << 1),
+    /** 
+     * Normalize tree by consolidating adjacent text nodes.
+     */
+    CMDocumentOptionsNormalize = (1 << 3),
+    /** 
+     * Convert straight quotes to curly, --- to em dashes, -- to en dashes.
+     */
+    CMDocumentOptionsSmart = (1 << 4)
+};
+
+/**
+ *  A Markdown document conforming to the CommonMark spec.
+ */
+@interface CMDocument : NSObject
+
+/**
+ *  Root node of the document.
+ */
+@property (nonatomic, readonly) CMNode *rootNode;
+
+/**
+ *  Initializes the receiver with a string.
+ *
+ *  @param string Markdown document string.
+ *  @param options Document options.
+ *
+ *  @return An initialized instance of the receiver.
+ */
+- (instancetype)initWithString:(NSString *)string options:(CMDocumentOptions)options;
+
+/**
+ *  Initializes the receiver with data.
+ *
+ *  @param data Markdown document data.
+ *  @param options Document options.
+ *
+ *  @return An initialized instance of the receiver.
+ */
+- (instancetype)initWithData:(NSData *)data options:(CMDocumentOptions)options;
+
+/**
+ *  Initializes the receiver with data read from a file.
+ *
+ *  @param path The file path to read from.
+ *  @param options Document options.
+ *
+ *  @return An initialized instance of the receiver, or `nil` if the file
+ *  could not be opened.
+ */
+- (instancetype)initWithContentsOfFile:(NSString *)path options:(CMDocumentOptions)options;
+
+
+/**
+ *  Base URL for links and images in the document.
+ *
+ *  Used as a base when a link destination is a scheme-less path (relative or absolute).
+ *
+ *  If the document has been created using `-[initWithContentsOfFile:options]`, linkBaseURL defaults to the document file's parent directory.
+ */
+@property (nonatomic) NSURL *linksBaseURL;
+
+/**
+ *  Get the absolute URL for a link or image node based on the documents's link base URL if needed
+ *
+ *  @param node Markdown document data.
+ *
+ *  @return the actual target URL of the node taking into account the documents's link base URL
+ */
+- (NSURL*) targetURLForNode:(CMNode *)node;
+
+@end

+ 74 - 0
CocoaMarkdown/CMDocument.m

@@ -0,0 +1,74 @@
+//
+//  CMDocument.m
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/12/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMDocument_Private.h"
+#import "CMNode_Private.h"
+
+@implementation CMDocument
+
+- (instancetype)initWithString:(NSString *)string options:(CMDocumentOptions)options
+{
+    if (string != nil) {
+        if ((self = [super init])) {
+            const char* utf8String = string.UTF8String;
+            cmark_node *node = cmark_parse_document(utf8String, strlen(utf8String), options);
+            if (node == NULL) return nil;
+            
+            _rootNode = [[CMNode alloc] initWithNode:node freeWhenDone:YES];
+            _options = options;
+        }
+    } else {
+        self = nil;
+    }
+    return self;
+}
+
+- (instancetype)initWithData:(NSData *)data options:(CMDocumentOptions)options
+{
+    NSParameterAssert(data);
+    
+    if ((self = [super init])) {
+        cmark_node *node = cmark_parse_document((const char *)data.bytes, data.length, options);
+        if (node == NULL) return nil;
+        
+        _rootNode = [[CMNode alloc] initWithNode:node freeWhenDone:YES];
+        _options = options;
+    }
+    return self;
+}
+
+- (instancetype)initWithContentsOfFile:(NSString *)path options:(CMDocumentOptions)options
+{
+    if ((self = [super init])) {
+        FILE *fp = fopen(path.UTF8String, "r");
+        if (fp == NULL) return nil;
+        
+        cmark_node *node = cmark_parse_file(fp, options);
+        fclose(fp);
+        if (node == NULL) return nil;
+        
+        _rootNode = [[CMNode alloc] initWithNode:node freeWhenDone:YES];
+        _options = options;
+        
+        _linksBaseURL = [NSURL fileURLWithPath:path.stringByDeletingLastPathComponent];
+    }
+    return self;
+}
+
+- (NSURL*) targetURLForNode:(CMNode *)node
+{
+    NSURL* nodeTargetUrl = node.URL;
+    if ((nodeTargetUrl.scheme == nil) && (_linksBaseURL != nil))
+    {
+        // If the node URL doesn't have a scheme, consider it relative to the base URL
+        nodeTargetUrl = [NSURL URLWithString:node.URLString relativeToURL:_linksBaseURL].absoluteURL;
+    }
+    return nodeTargetUrl;
+}
+
+@end

+ 16 - 0
CocoaMarkdown/CMDocument_Private.h

@@ -0,0 +1,16 @@
+//
+//  CMDocument_Private.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 4/27/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMDocument.h"
+
+@interface CMDocument ()
+/**
+ *  Options passed in at initialization time.
+ */
+@property (nonatomic, readonly) CMDocumentOptions options;
+@end

+ 20 - 0
CocoaMarkdown/CMHTMLElement.h

@@ -0,0 +1,20 @@
+//
+//  CMHTMLElement.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/16/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@protocol CMHTMLElementTransformer;
+
+@interface CMHTMLElement : NSObject
+@property (nonatomic, readonly) id<CMHTMLElementTransformer> transformer;
+@property (nonatomic, readonly) NSString *tagName;
+@property (nonatomic, readonly) NSMutableString *buffer;
+
+- (instancetype)initWithTransformer:(id<CMHTMLElementTransformer>)transformer;
+
+@end

+ 28 - 0
CocoaMarkdown/CMHTMLElement.m

@@ -0,0 +1,28 @@
+//
+//  CMHTMLElement.m
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/16/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMHTMLElement.h"
+#import "CMHTMLElementTransformer.h"
+
+@implementation CMHTMLElement
+
+- (instancetype)initWithTransformer:(id<CMHTMLElementTransformer>)transformer
+{
+    if ((self = [super init])) {
+        _transformer = transformer;
+        _buffer = [[NSMutableString alloc] init];
+    }
+    return self;
+}
+
+- (NSString *)tagName;
+{
+    return [_transformer.class tagName];
+}
+
+@end

+ 39 - 0
CocoaMarkdown/CMHTMLElementTransformer.h

@@ -0,0 +1,39 @@
+//
+//  CMHTMLElementTransformer.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/16/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class ONOXMLElement;
+
+/**
+ *  Interface for an object that can transform an HTML element to an attributed string.
+ */
+@protocol CMHTMLElementTransformer <NSObject>
+/**
+ *  @return The name of the tag that this transformer handles.
+ */
++ (NSString *)tagName;
+
+/**
+ *  Transforms an HTML element to an attributed string.
+ *
+ *  @param document   The HTML element to transform.
+ *  @param attributes The base attributes to be applied to the attributed string.
+ *
+ *  @return An attributed string.
+ */
+- (NSAttributedString *)attributedStringForElement:(ONOXMLElement *)element attributes:(NSDictionary *)attributes;
+
+@end
+
+/**
+ *  Use this macro inside an implementation of `-attributedStringForElement:attributes:`
+ *  to assert that the root element's tag matches the transformer's tag.
+ */
+#define CMAssertCorrectTag(element) \
+    NSAssert([element.tag isEqualToString:self.class.tagName], @"Tag does not match");

+ 36 - 0
CocoaMarkdown/CMHTMLRenderer.h

@@ -0,0 +1,36 @@
+//
+//  CMHTMLRenderer.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/20/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class CMDocument;
+
+/**
+ *  Renders HTML from a Markdown document.
+ */
+@interface CMHTMLRenderer : NSObject
+
+/**
+ *  Designated initializer.
+ *
+ *  @param document A Markdown document.
+ *  @param options  Rendering options.
+ *
+ *  @return An initialized instance of the receiver.
+ */
+- (instancetype)initWithDocument:(CMDocument *)document;
+
+/**
+ *  Renders HTML from the Markdown document.
+ *
+ *  @return A string containing the HTML representation
+ *  of the document.
+ */
+- (NSString *)render;
+
+@end

+ 31 - 0
CocoaMarkdown/CMHTMLRenderer.m

@@ -0,0 +1,31 @@
+//
+//  CMHTMLRenderer.m
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/20/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMHTMLRenderer.h"
+#import "CMDocument_Private.h"
+#import "CMNode_Private.h"
+
+@implementation CMHTMLRenderer {
+    CMDocument *_document;
+}
+
+- (instancetype)initWithDocument:(CMDocument *)document
+{
+    if ((self = [super init])) {
+        _document = document;
+    }
+    return self;
+}
+
+- (NSString *)render
+{
+    char *html = cmark_render_html(_document.rootNode.node, _document.options);
+    return [NSString stringWithUTF8String:html];
+}
+
+@end

+ 13 - 0
CocoaMarkdown/CMHTMLScriptTransformer.h

@@ -0,0 +1,13 @@
+//
+//  CMHTMLScriptTransformer.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/16/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "CMHTMLElementTransformer.h"
+
+@interface CMHTMLScriptTransformer : NSObject <CMHTMLElementTransformer>
+@end

+ 67 - 0
CocoaMarkdown/CMHTMLScriptTransformer.m

@@ -0,0 +1,67 @@
+//
+//  CMHTMLScriptTransformer.m
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/16/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMHTMLScriptTransformer.h"
+#import "CMHTMLScriptTransformer_Private.h"
+#import "CMPlatformDefines.h"
+
+#import "Ono.h"
+
+#if TARGET_OS_IPHONE
+#import <CoreText/CTStringAttributes.h>
+#endif
+
+@implementation CMHTMLScriptTransformer {
+    CMHTMLScriptStyle _style;
+    CGFloat _fontSizeRatio;
+    CGFloat _baselineOffset;
+}
+
+- (instancetype)initWithStyle:(CMHTMLScriptStyle)style fontSizeRatio:(CGFloat)ratio baselineOffset:(CGFloat)offset
+{
+    if ((self = [super init])) {
+        _style = style;
+        _fontSizeRatio = ratio;
+        _baselineOffset = offset;
+    }
+    return self;
+}
+
+#pragma mark - CMHTMLElementTransformer
+
++ (NSString *)tagName
+{
+    NSAssert(NO, @"Must be implemented by a subclass");
+    return nil;
+}
+
+- (NSAttributedString *)attributedStringForElement:(ONOXMLElement *)element attributes:(NSDictionary *)attributes
+{
+    CMAssertCorrectTag(element);
+    
+    NSMutableDictionary *allAttributes = [attributes mutableCopy];
+    NSString *superscriptAttribute = nil;
+#if TARGET_OS_IPHONE
+    superscriptAttribute = (__bridge NSString *)kCTSuperscriptAttributeName;
+#else
+    superscriptAttribute = NSSuperscriptAttributeName;
+#endif
+    allAttributes[superscriptAttribute] = @(_style);
+    CMFont *font = attributes[NSFontAttributeName];
+    if (font != nil) {
+        font = [CMFont fontWithDescriptor:font.fontDescriptor size:font.pointSize * _fontSizeRatio];
+        allAttributes[NSFontAttributeName] = font;
+    }
+    if (_baselineOffset != 0.0) {
+        allAttributes[NSBaselineOffsetAttributeName] = @(_baselineOffset);
+    }
+
+    return [[NSAttributedString alloc] initWithString:element.stringValue attributes:allAttributes];
+}
+
+@end

+ 24 - 0
CocoaMarkdown/CMHTMLScriptTransformer_Private.h

@@ -0,0 +1,24 @@
+//
+//  CMHTMLScriptTransformer_Private.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/16/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMHTMLScriptTransformer.h"
+
+#if TARGET_OS_IPHONE
+#import <UIKit/UIKit.h>
+#else
+#import <Cocoa/Cocoa.h>
+#endif
+
+typedef NS_ENUM(NSInteger, CMHTMLScriptStyle) {
+    CMHTMLScriptStyleSubscript = -1,
+    CMHTMLScriptStyleSuperscript = 1
+};
+
+@interface CMHTMLScriptTransformer ()
+- (instancetype)initWithStyle:(CMHTMLScriptStyle)style fontSizeRatio:(CGFloat)ratio baselineOffset:(CGFloat)offset;
+@end

+ 37 - 0
CocoaMarkdown/CMHTMLStrikethroughTransformer.h

@@ -0,0 +1,37 @@
+//
+//  CMHTMLStrikethroughTransformer.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/16/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "CMHTMLElementTransformer.h"
+#import "CMPlatformDefines.h"
+
+/**
+ Transforms HTML strikethrough elements (<s>) into attributed strings.
+ */
+@interface CMHTMLStrikethroughTransformer : NSObject <CMHTMLElementTransformer>
+
+/**
+ *  Initializes the receiver with the default attributes -- a single line
+ *  style and a color that matches the color of the text.
+ *
+ *  @return An initialized instance of the receiver.
+ */
+- (instancetype)init;
+
+/**
+ *  Initializes the receiver with a custom style and color.
+ *
+ *  @param style Strikethrough style.
+ *  @param color Strikethrough color. If `nil`, the transformer uses
+ *  the color of the text if it has been specified.
+ *
+ *  @return An initialized instance of the receiver.
+ */
+- (instancetype)initWithStrikethroughStyle:(CMUnderlineStyle)style color:(CMColor *)color;
+
+@end

+ 50 - 0
CocoaMarkdown/CMHTMLStrikethroughTransformer.m

@@ -0,0 +1,50 @@
+//
+//  CMHTMLStrikethroughTransformer.m
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/16/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMHTMLStrikethroughTransformer.h"
+#import "Ono.h"
+
+@implementation CMHTMLStrikethroughTransformer {
+    CMUnderlineStyle _style;
+    CMColor *_color;
+}
+
+- (instancetype)init
+{
+    return [self initWithStrikethroughStyle:NSUnderlineStyleSingle color:nil];
+}
+
+- (instancetype)initWithStrikethroughStyle:(CMUnderlineStyle)style color:(CMColor *)color
+{
+    if ((self = [super init])) {
+        _style = style;
+        _color = color;
+    }
+    return self;
+}
+
+#pragma mark - CMHTMLElementTransformer
+
++ (NSString *)tagName { return @"s"; };
+
+- (NSAttributedString *)attributedStringForElement:(ONOXMLElement *)element attributes:(NSDictionary *)attributes
+{
+    CMAssertCorrectTag(element);
+    
+    NSMutableDictionary *allAttributes = [attributes mutableCopy];
+    allAttributes[NSStrikethroughStyleAttributeName] = @(_style);
+    
+    CMColor *color = _color ?: allAttributes[NSForegroundColorAttributeName];
+    if (color != nil) {
+        allAttributes[NSStrikethroughColorAttributeName] = color;
+    }
+    
+    return [[NSAttributedString alloc] initWithString:element.stringValue attributes:allAttributes];
+}
+
+@end

+ 51 - 0
CocoaMarkdown/CMHTMLSubscriptTransformer.h

@@ -0,0 +1,51 @@
+//
+//  CMHTMLSubscriptTransformer.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/16/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "CMHTMLScriptTransformer.h"
+
+#if TARGET_OS_IPHONE
+#import <UIKit/UIKit.h>
+#else
+#import <Cocoa/Cocoa.h>
+#endif
+
+/**
+ *  Transforms HTML subscript elements (<sub>) into attributed strings.
+ */
+@interface CMHTMLSubscriptTransformer : CMHTMLScriptTransformer
+
+/**
+ *  Initializes the receiver with the default font ratio (0.7)
+ *
+ *  @return An initialized instance of the receiver.
+ */
+- (instancetype)init;
+
+/**
+ *  Initializes the receiver with a custom font size ratio.
+ *
+ *  @param ratio The factor to multiply the existing font point
+ *  size by to calculate the size of the subscript font.
+ *
+ *  @return An initialized instance of the receiver.
+ */
+- (instancetype)initWithFontSizeRatio:(CGFloat)ratio;
+
+/**
+ *  Initializes the receiver with a custom font size ratio and a custom baseline offset.
+ *
+ *  @param ratio The factor to multiply the existing font point
+ *  size by to calculate the size of the superscript font.
+ *  @param offset The offset for the baseline of the subscript.
+ *
+ *  @return An initialized instance of the receiver.
+ */
+- (instancetype)initWithFontSizeRatio:(CGFloat)ratio baselineOffset:(CGFloat)offset;
+
+@end

+ 33 - 0
CocoaMarkdown/CMHTMLSubscriptTransformer.m

@@ -0,0 +1,33 @@
+//
+//  CMHTMLSubscriptTransformer.m
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/16/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMHTMLSubscriptTransformer.h"
+#import "CMHTMLScriptTransformer_Private.h"
+
+@implementation CMHTMLSubscriptTransformer
+
+- (instancetype)init
+{
+    return [self initWithFontSizeRatio:0.7];
+}
+
+- (instancetype)initWithFontSizeRatio:(CGFloat)ratio
+{
+    return [self initWithStyle:CMHTMLScriptStyleSubscript fontSizeRatio:ratio baselineOffset:0.0];
+}
+
+- (instancetype)initWithFontSizeRatio:(CGFloat)ratio baselineOffset:(CGFloat)offset
+{
+    return [super initWithStyle:CMHTMLScriptStyleSubscript fontSizeRatio:ratio baselineOffset:offset];
+}
+
+#pragma mark - CMHTMLElementTransformer
+
++ (NSString *)tagName { return @"sub"; };
+
+@end

+ 51 - 0
CocoaMarkdown/CMHTMLSuperscriptTransformer.h

@@ -0,0 +1,51 @@
+//
+//  CMHTMLSuperscriptTransformer.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/16/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "CMHTMLScriptTransformer.h"
+
+#if TARGET_OS_IPHONE
+#import <UIKit/UIKit.h>
+#else
+#import <Cocoa/Cocoa.h>
+#endif
+
+/**
+ *  Transforms HTML superscript elements (<sup>) into attributed strings.
+ */
+@interface CMHTMLSuperscriptTransformer : CMHTMLScriptTransformer
+
+/**
+ *  Initializes the receiver with the default font ratio (0.7)
+ *
+ *  @return An initialized instance of the receiver.
+ */
+- (instancetype)init;
+
+/**
+ *  Initializes the receiver with a custom font size ratio and a default baseline offset.
+ *
+ *  @param ratio The factor to multiply the existing font point
+ *  size by to calculate the size of the superscript font.
+ *
+ *  @return An initialized instance of the receiver.
+ */
+- (instancetype)initWithFontSizeRatio:(CGFloat)ratio;
+
+/**
+ *  Initializes the receiver with a custom font size ratio and a custom baseline offset.
+ *
+ *  @param ratio The factor to multiply the existing font point
+ *  size by to calculate the size of the superscript font.
+ *  @param offset The offset for the baseline of the superscript.
+ *
+ *  @return An initialized instance of the receiver.
+ */
+- (instancetype)initWithFontSizeRatio:(CGFloat)ratio baselineOffset:(CGFloat)offset;
+
+@end

+ 33 - 0
CocoaMarkdown/CMHTMLSuperscriptTransformer.m

@@ -0,0 +1,33 @@
+//
+//  CMHTMLSuperscriptTransformer.m
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/16/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMHTMLSuperscriptTransformer.h"
+#import "CMHTMLScriptTransformer_Private.h"
+
+@implementation CMHTMLSuperscriptTransformer
+
+- (instancetype)init
+{
+    return [self initWithFontSizeRatio:0.7];
+}
+
+- (instancetype)initWithFontSizeRatio:(CGFloat)ratio
+{
+    return [self initWithStyle:CMHTMLScriptStyleSuperscript fontSizeRatio:ratio baselineOffset:0.0];
+}
+
+- (instancetype)initWithFontSizeRatio:(CGFloat)ratio baselineOffset:(CGFloat)offset
+{
+    return [super initWithStyle:CMHTMLScriptStyleSuperscript fontSizeRatio:ratio baselineOffset:offset];
+}
+
+#pragma mark - CMHTMLElementTransformer
+
++ (NSString *)tagName { return @"sup"; };
+
+@end

+ 37 - 0
CocoaMarkdown/CMHTMLUnderlineTransformer.h

@@ -0,0 +1,37 @@
+//
+//  CMHTMLUnderlineTransformer.h
+//  CocoaMarkdown
+//
+//  Created by Damien Rambout on 19/01/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "CMHTMLElementTransformer.h"
+#import "CMPlatformDefines.h"
+
+/**
+ Transforms HTML underline elements (<u>) into attributed strings.
+ */
+@interface CMHTMLUnderlineTransformer : NSObject <CMHTMLElementTransformer>
+
+/**
+ *  Initializes the receiver with the default attributes -- a single line
+ *  style and a color that matches the color of the text.
+ *
+ *  @return An initialized instance of the receiver.
+ */
+- (instancetype)init;
+
+/**
+ *  Initializes the receiver with a custom style and color.
+ *
+ *  @param style Strikethrough style.
+ *  @param color Strikethrough color. If `nil`, the transformer uses
+ *  the color of the text if it has been specified.
+ *
+ *  @return An initialized instance of the receiver.
+ */
+- (instancetype)initWithUnderlineStyle:(CMUnderlineStyle)style color:(CMColor *)color;
+
+@end

+ 50 - 0
CocoaMarkdown/CMHTMLUnderlineTransformer.m

@@ -0,0 +1,50 @@
+//
+//  CMHTMLUnderlineTransformer.m
+//  CocoaMarkdown
+//
+//  Created by Damien Rambout on 21/01/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMHTMLUnderlineTransformer.h"
+#import "Ono.h"
+
+@implementation CMHTMLUnderlineTransformer {
+    CMUnderlineStyle _style;
+    CMColor *_color;
+}
+
+- (instancetype)init
+{
+    return [self initWithUnderlineStyle:NSUnderlineStyleSingle color:nil];
+}
+
+- (instancetype)initWithUnderlineStyle:(CMUnderlineStyle)style color:(CMColor *)color
+{
+    if ((self = [super init])) {
+        _style = style;
+        _color = color;
+    }
+    return self;
+}
+
+#pragma mark - CMHTMLElementTransformer
+
++ (NSString *)tagName { return @"u"; };
+
+- (NSAttributedString *)attributedStringForElement:(ONOXMLElement *)element attributes:(NSDictionary *)attributes
+{
+    CMAssertCorrectTag(element);
+    
+    NSMutableDictionary *allAttributes = [attributes mutableCopy];
+    allAttributes[NSUnderlineStyleAttributeName] = @(_style);
+    
+    CMColor *color = _color ?: allAttributes[NSForegroundColorAttributeName];
+    if (color != nil) {
+        allAttributes[NSUnderlineColorAttributeName] = color;
+    }
+    
+    return [[NSAttributedString alloc] initWithString:element.stringValue attributes:allAttributes];
+}
+
+@end

+ 14 - 0
CocoaMarkdown/CMHTMLUtilities.h

@@ -0,0 +1,14 @@
+//
+//  CMHTMLUtilities.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/16/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+NSString * CMTagNameFromHTMLTag(NSString *tag);
+BOOL CMIsHTMLVoidTagName(NSString *name);
+BOOL CMIsHTMLTag(NSString *tag);
+BOOL CMIsHTMLClosingTag(NSString *tag);

+ 61 - 0
CocoaMarkdown/CMHTMLUtilities.m

@@ -0,0 +1,61 @@
+//
+//  CMHTMLUtilities.m
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/16/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMHTMLUtilities.h"
+
+NSString * CMTagNameFromHTMLTag(NSString *tag)
+{
+    // Assumes a well-formed HTML tag.
+    NSCharacterSet *alphanumeric = NSCharacterSet.alphanumericCharacterSet;
+    NSUInteger start = [tag rangeOfCharacterFromSet:alphanumeric].location;
+    if (start == NSNotFound) return nil;
+    
+    NSCharacterSet *inverseAlphanumeric = alphanumeric.invertedSet;
+    NSUInteger end = [tag rangeOfCharacterFromSet:inverseAlphanumeric options:0 range:NSMakeRange(start, tag.length - start)].location;
+    if (end == NSNotFound) return nil;
+    
+    return [tag substringWithRange:NSMakeRange(start, end - start)];
+}
+
+BOOL CMIsHTMLVoidTagName(NSString *name)
+{
+    static NSSet *voidNames = nil;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        voidNames = [NSSet setWithObjects:
+            @"area",
+            @"base",
+            @"br",
+            @"col",
+            @"command",
+            @"embed",
+            @"hr",
+            @"img",
+            @"input",
+            @"keygen",
+            @"link",
+            @"meta",
+            @"param",
+            @"source",
+            @"track",
+            @"wbr",
+            nil
+        ];
+    });
+    return [voidNames containsObject:name];
+}
+
+BOOL CMIsHTMLTag(NSString *tag)
+{
+    return [tag hasPrefix:@"<"] && [tag hasSuffix:@">"];
+}
+
+BOOL CMIsHTMLClosingTag(NSString *tag)
+{
+    return [tag hasPrefix:@"</"] && [tag hasSuffix:@">"];
+}

+ 26 - 0
CocoaMarkdown/CMImageTextAttachment.h

@@ -0,0 +1,26 @@
+//
+//  CMImageTextAttachment.h
+//  CocoaMarkdown
+//
+//  Created by Jean-Luc on 10/05/2019.
+//  Copyright © 2019 Indragie Karunaratne. All rights reserved.
+//
+@import Foundation;
+
+#if TARGET_OS_IPHONE
+@import UIKit;
+#else
+@import Cocoa;
+#endif
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface CMImageTextAttachment : NSTextAttachment
+
+- (instancetype) initWithImageURL:(NSURL*)imageURL;
+
+@property (nonatomic, readonly) NSURL* imageURL;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 237 - 0
CocoaMarkdown/CMImageTextAttachment.m

@@ -0,0 +1,237 @@
+//
+//  CMImageTextAttachment.m
+//  CocoaMarkdown
+//
+//  Created by Jean-Luc Jumpertz on 10/05/2019.
+//  Inspired by https://www.cocoanetics.com/2016/09/asynchronous-nstextattachments-22/
+//  Copyright © 2019 Jean-Luc Jumpertz. All rights reserved.
+//
+
+#import "CMImageTextAttachment.h"
+
+#if TARGET_OS_IPHONE
+#import <MobileCoreServices/MobileCoreServices.h> // For UTType...
+#endif
+
+@interface CMImageTextAttachment ()
+{
+    NSTextContainer* __weak _textContainer;
+    NSURLSessionDataTask* _downloadTask;
+    BOOL _isImageLoaded;
+}
+
+@end
+
+@interface NSLayoutManager (CMImageTextAttachment)
+
+- (void) setNeedsDisplayForAttachment:(NSTextAttachment*)textAttachment;
+- (void) setNeedsLayoutForAttachment:(NSTextAttachment*)textAttachment;
+
+@end
+
+@implementation CMImageTextAttachment
+
+static CGSize _placeholderImageSize = {16, 16};
+static CGFloat _placeholderImageCornerRadius = 3.0;
+
+#if TARGET_OS_IPHONE
+static UIImage* _placeholderImage;
+
++ (UIImage*) placeholderImage
+{
+    if (_placeholderImage == nil) {
+        UIGraphicsBeginImageContextWithOptions(_placeholderImageSize, NO, 0);
+        
+        CGRect imageRect = CGRectMake(0, 0, _placeholderImageSize.width, _placeholderImageSize.height);
+        UIBezierPath* placeholderShape = [UIBezierPath bezierPathWithRoundedRect: imageRect cornerRadius:(CGFloat)_placeholderImageCornerRadius];
+        [UIColor.lightGrayColor setFill];
+        [placeholderShape fill];
+        [UIColor.grayColor setStroke];
+        [placeholderShape stroke];
+        [@"?" drawInRect:CGRectInset(imageRect, 4, -1) withAttributes:@{ NSFontAttributeName: [UIFont systemFontOfSize:imageRect.size.height - 2],
+                                                                         NSForegroundColorAttributeName: UIColor.whiteColor }];
+        _placeholderImage = UIGraphicsGetImageFromCurrentImageContext();
+        
+        UIGraphicsEndImageContext();
+    }
+    return _placeholderImage;
+}
+
+#else
+
+static NSImage* _placeholderImage;
+
++ (NSImage*) placeholderImage
+{
+    if (_placeholderImage == nil) {
+        _placeholderImage = [NSImage imageWithSize:_placeholderImageSize flipped:NO drawingHandler:^BOOL(NSRect dstRect) {
+            NSBezierPath* placeholderShape = [NSBezierPath bezierPathWithRoundedRect:dstRect xRadius:_placeholderImageCornerRadius yRadius:_placeholderImageCornerRadius];
+            [NSColor.lightGrayColor setFill];
+            [placeholderShape fill];
+            [NSColor.grayColor setStroke];
+            [placeholderShape stroke];
+            [@"?" drawInRect:CGRectInset(dstRect, 4, -1) withAttributes:@{ NSFontAttributeName: [NSFont systemFontOfSize:dstRect.size.height - 2],
+                                                                           NSForegroundColorAttributeName: NSColor.whiteColor }];
+            return YES;
+        }];
+    }
+    return _placeholderImage;
+}
+#endif
+
+- (instancetype) initWithImageURL:(NSURL*)imageURL
+{
+    NSString* imageUrlUti = (__bridge_transfer NSString*) UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)imageURL.pathExtension, kUTTypeData);
+    
+    self = [super initWithData:nil ofType:imageUrlUti];
+    if (self != nil) {
+        _imageURL = imageURL;
+        _isImageLoaded = NO;
+        self.image = [self.class placeholderImage];
+    }
+    return self;
+}
+
+
+- (CGRect) attachmentBoundsForTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(CGRect)lineFrag glyphPosition:(CGPoint)position characterIndex:(NSUInteger)charIndex
+{
+    CGSize attachmentImageSize = self.image.size;
+    
+    CGFloat maxWidth = lineFrag.size.width * 0.95;
+    if (attachmentImageSize.width > maxWidth) {
+        attachmentImageSize = CGSizeMake(maxWidth, attachmentImageSize.height * maxWidth / attachmentImageSize.width);
+    }
+    
+    CGRect attachmentBounds;
+    attachmentBounds.origin = CGPointZero;
+    attachmentBounds.size = attachmentImageSize;
+    return attachmentBounds;
+}
+
+#if TARGET_OS_IPHONE
+- (nullable UIImage *)imageForBounds:(CGRect)imageBounds textContainer:(nullable NSTextContainer *)textContainer characterIndex:(NSUInteger)charIndex
+#else
+- (nullable NSImage *)imageForBounds:(NSRect)imageBounds textContainer:(nullable NSTextContainer *)textContainer characterIndex:(NSUInteger)charIndex
+#endif
+{    
+    if (! _isImageLoaded && (_imageURL != nil)) {
+        
+        // Save a reference to the textcontainer
+        _textContainer = textContainer;
+        
+        // Load the image asynchronously
+        if (_imageURL.isFileURL) {
+            dispatch_async(dispatch_get_main_queue(), ^{
+                
+                NSData* imageData = [NSData dataWithContentsOfURL:_imageURL];
+                if (imageData.length > 0) {
+                    [self setImageWithData:imageData];
+                    _isImageLoaded = YES;
+                }
+            });
+        }
+        else if (_downloadTask == nil) {
+            // Not a file URL and no download task in progress: use an URL-data-task to get the data
+            _downloadTask = [NSURLSession.sharedSession dataTaskWithURL:_imageURL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
+                
+                if ((error == nil) && (data.length > 0)) {
+                    dispatch_async(dispatch_get_main_queue(), ^{
+                        [self setImageWithData:data];
+                        _isImageLoaded = YES;
+                    });
+                }
+                
+                _downloadTask = nil;
+            }];
+            
+            [_downloadTask resume];
+        }
+    }    
+    
+#if !TARGET_OS_IPHONE
+#ifdef __MAC_10_15
+    if (! [NSProcessInfo.processInfo isOperatingSystemAtLeastVersion: (NSOperatingSystemVersion){10, 15, 0}]) 
+#endif
+    {
+        // On macOS 10.14.6 and below, the image attachment is dislayed vertically flipped, so we need to flip it again to display it correctly
+        // This issue has been fixed on macOS 10.15 for applications compiled with Xcode 11 or later (SDK version >= 10.15)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+        [self.image setFlipped:NSGraphicsContext.currentContext.isFlipped];
+#pragma clang diagnostic pop
+    }
+#endif
+    
+    return self.image;
+}
+
+- (void) setImageWithData:(NSData*)imageData
+{
+    NSString* imageUti = (__bridge_transfer NSString*) UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)_imageURL.pathExtension, kUTTypeData);
+    self.fileType = imageUti;
+    self.contents = imageData;
+    
+    CGSize currentImageSize = self.image.size;
+    
+#if TARGET_OS_IPHONE
+    self.image = [UIImage imageWithData: imageData];
+#else
+    self.image = [[NSImage alloc] initWithData:imageData];
+#endif
+    
+    if (self.image != nil) {
+        if (! CGSizeEqualToSize(self.image.size, currentImageSize)) {
+             // The layout needs to be refreshed
+            [_textContainer.layoutManager setNeedsLayoutForAttachment:self];
+        }
+        else {
+            // The image display should be refreshed
+            [_textContainer.layoutManager setNeedsDisplayForAttachment:self];
+        }
+    }
+}
+
+@end
+
+#pragma mark - NSLayoutManager class extension
+
+@implementation NSLayoutManager (CMImageTextAttachment)
+
+/// Trigger a re-display for an attachment
+- (void) setNeedsDisplayForAttachment:(NSTextAttachment*)textAttachment
+{
+    NSArray<NSValue*>* rangesForAttachment = [self rangesForAttachment:textAttachment];
+    
+    [rangesForAttachment enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSValue * _Nonnull rangeObject, NSUInteger idx, BOOL * _Nonnull stop) {
+        // invalidate the display for the range
+        [self invalidateDisplayForCharacterRange:rangeObject.rangeValue];
+    }];
+}
+
+/// Trigger a relayout for an attachment
+- (void) setNeedsLayoutForAttachment:(NSTextAttachment*)textAttachment
+{
+    NSArray<NSValue*>* rangesForAttachment = [self rangesForAttachment:textAttachment];
+    
+    [rangesForAttachment enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSValue * _Nonnull rangeObject, NSUInteger idx, BOOL * _Nonnull stop) {
+        // invalidate the layout for the range
+        [self invalidateLayoutForCharacterRange:rangeObject.rangeValue actualCharacterRange:NULL];
+        // also need to trigger re-display or already visible images might not get updated
+        [self invalidateDisplayForCharacterRange:rangeObject.rangeValue];
+    }];
+}
+
+- (NSArray<NSValue*>*) rangesForAttachment:(NSTextAttachment*)textAttachment
+{
+    NSMutableArray<NSValue*>* rangesForAttachment = [NSMutableArray new];
+    
+    [self.textStorage enumerateAttribute:NSAttachmentAttributeName inRange:NSMakeRange(0, self.textStorage.length) options:0 usingBlock:^(id  _Nullable value, NSRange range, BOOL * _Nonnull stop) {
+        if (value == textAttachment) {
+            [rangesForAttachment addObject:[NSValue valueWithRange:range]];
+        }
+    }];
+    
+    return rangesForAttachment;
+}
+
+@end

+ 63 - 0
CocoaMarkdown/CMIterator.h

@@ -0,0 +1,63 @@
+//
+//  CMIterator.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/13/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+typedef NS_ENUM(NSInteger, CMEventType) {
+    CMEventTypeNone,
+    CMEventTypeDone,
+    CMEventTypeEnter,
+    CMEventTypeExit
+};
+
+@class CMNode;
+
+/**
+ *  Walks through a tree of nodes.
+ */
+@interface CMIterator : NSObject
+
+/**
+ *  Initializes the receiver with a root node.
+ *
+ *  @param rootNode Root node to start traversing from.
+ *
+ *  @return An initialized instance of the receiver.
+ */
+- (instancetype)initWithRootNode:(CMNode *)rootNode;
+
+/**
+ *  Returns the current node.
+ */
+@property (readonly) CMNode *currentNode;
+
+/**
+ *  Returns the current event type.
+ */
+@property (readonly) CMEventType currentEvent;
+
+/**
+ *  Walks through a tree of nodes, starting from the root node.
+ *
+ *  @discussion See the section on iterators in cmark.h for more details.
+ *
+ *  @param block Block to call upon entering or exiting a node during traversal.
+ *  Set `stop` to `YES` to stop iteration.
+ */
+- (void)enumerateUsingBlock:(void (^)(CMNode *node, CMEventType event, BOOL *stop))block;
+
+/**
+ *  Resets the iterator to the specified node and event. The node must be either
+ *  the root node or a child of the root node.
+ *
+ *  @param node      The node to reset to.
+ *  @param eventType The event to reset to.
+ */
+- (void)resetToNode:(CMNode *)node withEventType:(CMEventType)eventType;
+
+@end

+ 67 - 0
CocoaMarkdown/CMIterator.m

@@ -0,0 +1,67 @@
+//
+//  CMIterator.m
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/13/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMIterator.h"
+#import "CMNode_Private.h"
+
+@implementation CMIterator {
+    cmark_iter *_iter;
+}
+
+#pragma mark - Initialization
+
+- (instancetype)initWithRootNode:(CMNode *)rootNode
+{
+    NSParameterAssert(rootNode);
+    
+    if ((self = [super init])) {
+        _iter = cmark_iter_new(rootNode.node);
+        if (_iter == NULL) return nil;
+    }
+    return self;
+}
+
+- (void)dealloc
+{
+    cmark_iter_free(_iter);
+}
+
+#pragma mark - Accessors
+
+- (CMNode *)currentNode
+{
+    return [[CMNode alloc] initWithNode:cmark_iter_get_node(_iter) freeWhenDone:NO];
+}
+
+- (CMEventType)currentEvent
+{
+    return (CMEventType)cmark_iter_get_event_type(_iter);
+}
+
+#pragma mark - Iteration
+
+- (void)enumerateUsingBlock:(void (^)(CMNode *node, CMEventType event, BOOL *stop))block
+{
+    NSParameterAssert(block);
+    
+    cmark_event_type event;
+    BOOL stop = NO;
+    
+    while ((event = cmark_iter_next(_iter)) != CMARK_EVENT_DONE) {
+        CMNode *currentNode = [[CMNode alloc] initWithNode:cmark_iter_get_node(_iter) freeWhenDone:NO];
+        block(currentNode, (CMEventType)event, &stop);
+        if (stop) break;
+    }
+}
+
+- (void)resetToNode:(CMNode *)node withEventType:(CMEventType)eventType
+{
+    cmark_iter_reset(_iter, node.node, (cmark_event_type)eventType);
+}
+
+@end

+ 181 - 0
CocoaMarkdown/CMNode.h

@@ -0,0 +1,181 @@
+//
+//  CMNode.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/12/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class CMIterator;
+
+typedef NS_ENUM(NSInteger, CMNodeType) {
+    /* Error status */
+    CMNodeTypeNone,
+    
+    /* Block */
+    CMNodeTypeDocument,
+    CMNodeTypeBlockQuote,
+    CMNodeTypeList,
+    CMNodeTypeItem,
+    CMNodeTypeCodeBlock,
+    CMNodeTypeHTML,
+    CMNodeTypeCustomBlock,
+    CMNodeTypeParagraph,
+    CMNodeTypeHeader,
+    CMNodeTypeHRule,
+    
+    CMNodeTypeFirstBlock = CMNodeTypeDocument,
+    CMNodeTypeLastBlock = CMNodeTypeHRule,
+    
+    /* Inline */
+    CMNodeTypeText,
+    CMNodeTypeSoftbreak,
+    CMNodeTypeLinebreak,
+    CMNodeTypeCode,
+    CMNodeTypeInlineHTML,
+    CMNodeTypeCustomInline,
+    CMNodeTypeEmphasis,
+    CMNodeTypeStrong,
+    CMNodeTypeLink,
+    CMNodeTypeImage,
+    
+    CMNodeTypeFirstInline = CMNodeTypeText,
+    CMNodeTypeLastInline = CMNodeTypeImage
+};
+
+typedef NS_ENUM(NSInteger, CMListType) {
+    CMListTypeNone,
+    CMListTypeUnordered,
+    CMListTypeOrdered
+};
+
+typedef NS_ENUM(NSInteger, CMDelimeterType) {
+    CMDelimeterTypeNone,
+    CMDelimeterTypePeriod,
+    CMDelimeterTypeParen
+};
+
+/**
+ *  Immutable interface to a CommonMark node.
+ */
+@interface CMNode : NSObject
+
+/**
+ *  Creates an iterator for the node tree that has the
+ *  receiver as its root.
+ *
+ *  @return A new iterator.
+ */
+- (CMIterator *)iterator;
+
+/**
+ *  The next node in the sequence, or `nil` if there is none.
+ */
+@property (readonly) CMNode *next;
+
+/**
+ *  The previous node in the sequence, or `nil` if there is none.
+ */
+@property (readonly) CMNode *previous;
+
+/**
+ *  The receiver's parent node, or `nil` if there is none.
+ */
+@property (readonly) CMNode *parent;
+
+/**
+ *  The first child node of the receiver, or `nil` if there is none.
+ */
+@property (readonly) CMNode *firstChild;
+
+/**
+ *  The last child node of the receiver, or `nil` if there is none.
+ */
+@property (readonly) CMNode *lastChild;
+
+/**
+ *  The type of the node, or `CMNodeTypeNone` on error.
+ */
+@property (readonly) CMNodeType type;
+
+/**
+ *  String representation of `type`.
+ */
+@property (readonly) NSString *humanReadableType;
+
+/**
+ *  String contents of the receiver, or `nil` if there is none.
+ */
+@property (readonly) NSString *stringValue;
+
+/**
+ *  Header level of the receiver, or `0` if the receiver is not a header.
+ */
+@property (readonly) NSInteger headerLevel;
+
+/**
+ *  Info string from a fenced code block, or `nil` if there is none.
+ */
+@property (readonly) NSString *fencedCodeInfo;
+
+/**
+ *  The receiver's list type, or `CMListTypeNone` if the receiver
+ *  is not a list.
+ */
+@property (readonly) CMListType listType;
+
+/**
+ *  The receiver's list delimeter type, or `CMDelimeterTypeNone` if the
+ *  receiver is not a list.
+ */
+@property (readonly) CMDelimeterType listDelimeterType;
+
+/**
+ *  Starting number of the list, or `0` if the receiver is not
+ *  an ordered list.
+ */
+@property (readonly) NSInteger listStartingNumber;
+
+/**
+ *  `YES` if the receiver is a tight list, `NO` otherwise.
+ */
+@property (readonly) BOOL listTight;
+
+/**
+ *  Link or image URL string (as set in the document), or `nil` if there is none.
+ */
+@property (readonly) NSString *URLString;
+
+/**
+ *  Link or image URL, or `nil` if there is none.
+ */
+@property (readonly) NSURL *URL;
+
+/**
+ *  Link or image title, or `nil` if there is none.
+ */
+@property (readonly) NSString *title;
+
+/**
+ *  The line on which the receiver begins.
+ */
+@property (readonly) NSInteger startLine;
+
+/**
+ *  The column on which the receiver begins.
+ */
+@property (readonly) NSInteger startColumn;
+
+/**
+ *  The line on which the receiver ends.
+ */
+@property (readonly) NSInteger endLine;
+
+/**
+ *  The column on which the receiver ends.
+ */
+@property (readonly) NSInteger endColumn;
+
+@end

+ 239 - 0
CocoaMarkdown/CMNode.m

@@ -0,0 +1,239 @@
+//
+//  CMNode.m
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/12/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMNode.h"
+#import "CMNode_Private.h"
+#import "CMIterator.h"
+
+static CMNode * wrap(cmark_node *node) {
+    if (node == NULL) return nil;
+    
+    return [[CMNode alloc] initWithNode:node freeWhenDone:NO];
+}
+
+static NSString * str(const char *buf) {
+    if (buf == NULL) return nil;
+    
+    return [NSString stringWithUTF8String:buf];
+}
+
+static NSString * NSStringFromCMNodeType(CMNodeType type) {
+    switch (type) {
+        case CMNodeTypeBlockQuote:
+            return @"Block Quote";
+        case CMNodeTypeCode:
+            return @"Code";
+        case CMNodeTypeCodeBlock:
+            return @"Code Block";
+        case CMNodeTypeDocument:
+            return @"Document";
+        case CMNodeTypeEmphasis:
+            return @"Emphasis";
+        case CMNodeTypeHeader:
+            return @"Header";
+        case CMNodeTypeHRule:
+            return @"Horizontal Rule";
+        case CMNodeTypeHTML:
+            return @"HTML";
+        case CMNodeTypeImage:
+            return @"Image";
+        case CMNodeTypeInlineHTML:
+            return @"Inline HTML";
+        case CMNodeTypeItem:
+            return @"List Item";
+        case CMNodeTypeLinebreak:
+            return @"Linebreak";
+        case CMNodeTypeLink:
+            return @"Link";
+        case CMNodeTypeList:
+            return @"List";
+        case CMNodeTypeNone:
+            return @"None";
+        case CMNodeTypeParagraph:
+            return @"Paragraph";
+        case CMNodeTypeSoftbreak:
+            return @"Softbreak";
+        case CMNodeTypeStrong:
+            return @"Strong";
+        case CMNodeTypeText:
+            return @"Text";
+        default: return nil;
+    }
+}
+
+@implementation CMNode {
+    BOOL _freeWhenDone;
+}
+
+#pragma mark - Initialization
+
+- (instancetype)init
+{
+    NSAssert(NO, @"CMNode instance can not be created.");
+    return nil;
+}
+
+- (instancetype)initWithNode:(cmark_node *)node freeWhenDone:(BOOL)free
+{
+    NSParameterAssert(node);
+    
+    if ((self = [super init])) {
+        _node = node;
+        _freeWhenDone = free;
+    }
+    return self;
+}
+
+- (void)dealloc
+{
+    if (_freeWhenDone) {
+        cmark_node_free(_node);
+    }
+}
+
+#pragma mark - NSObject
+
+- (BOOL)isEqual:(CMNode *)node
+{
+    if (self == node) return YES;
+    if (![node isMemberOfClass:self.class]) return NO;
+    
+    return _node == node->_node;
+}
+
+- (NSUInteger)hash
+{
+    return (NSUInteger)_node;
+}
+
+- (NSString *)description
+{
+    return [NSString stringWithFormat:@"<%@:%p type:%@ stringValue:%@>", self.class, self, NSStringFromCMNodeType(self.type), self.stringValue];
+}
+
+#pragma mark - Iteration
+
+- (CMIterator *)iterator
+{
+    return [[CMIterator alloc] initWithRootNode:self];
+}
+
+#pragma mark - Tree Traversal
+
+- (CMNode *)next
+{
+    return wrap(cmark_node_next(_node));
+}
+
+- (CMNode *)previous
+{
+    return wrap(cmark_node_previous(_node));
+}
+
+- (CMNode *)parent
+{
+    return wrap(cmark_node_parent(_node));
+}
+
+- (CMNode *)firstChild
+{
+    return wrap(cmark_node_first_child(_node));
+}
+
+- (CMNode *)lastChild
+{
+    return wrap(cmark_node_last_child(_node));
+}
+
+#pragma mark - Attributes
+
+- (CMNodeType)type
+{
+    return (CMNodeType)cmark_node_get_type(_node);
+}
+
+- (NSString *)humanReadableType
+{
+    return str(cmark_node_get_type_string(_node));
+}
+
+- (NSString *)stringValue
+{
+    return str(cmark_node_get_literal(_node));
+}
+
+- (NSInteger)headerLevel
+{
+    return cmark_node_get_header_level(_node);
+}
+
+- (NSString *)fencedCodeInfo
+{
+    return str(cmark_node_get_fence_info(_node));
+}
+
+- (CMListType)listType
+{
+    return (CMListType)cmark_node_get_list_type(_node);
+}
+
+- (CMDelimeterType)listDelimeterType
+{
+    return (CMDelimeterType)cmark_node_get_list_delim(_node);
+}
+
+- (NSInteger)listStartingNumber
+{
+    return cmark_node_get_list_start(_node);
+}
+
+- (BOOL)listTight
+{
+    return (cmark_node_get_list_tight(_node) == 0) ? NO : YES;
+}
+
+- (NSString *)URLString
+{
+    return str(cmark_node_get_url(_node));
+}
+
+- (NSURL *)URL
+{
+    NSString *URLString = str(cmark_node_get_url(_node));
+    if (URLString != nil) {
+        return [NSURL URLWithString:URLString];
+    }
+    return nil;
+}
+
+- (NSString *)title
+{
+    return str(cmark_node_get_title(_node));
+}
+
+- (NSInteger)startLine
+{
+    return cmark_node_get_start_line(_node);
+}
+
+- (NSInteger)startColumn
+{
+    return cmark_node_get_start_column(_node);
+}
+
+- (NSInteger)endLine
+{
+    return cmark_node_get_end_line(_node);
+}
+
+- (NSInteger)endColumn
+{
+    return cmark_node_get_end_column(_node);
+}
+
+@end

+ 25 - 0
CocoaMarkdown/CMNode_Private.h

@@ -0,0 +1,25 @@
+//
+//  CMNode_Private.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/13/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMNode.h"
+#import "cmark.h"
+
+@interface CMNode ()
+/**
+ *  Designated initializer.
+ *
+ *  @param node Pointer to the node to wrap.
+ *  @param free Whether to free the underlying node when the
+ *  receiver is deallocated.
+ *
+ *  @return An initialized instance of the receiver.
+ */
+- (instancetype)initWithNode:(cmark_node *)node freeWhenDone:(BOOL)free;
+
+@property (readonly) cmark_node *node;
+@end

+ 116 - 0
CocoaMarkdown/CMParser.h

@@ -0,0 +1,116 @@
+//
+//  CMParser.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/13/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class CMNode;
+
+@class CMDocument;
+@protocol CMParserDelegate;
+
+/**
+ *  Not really a parser, but you can pretend it is. 
+ *
+ *  This class takes a `CMDocument` (which contains the tree for the already-parsed 
+ *  Markdown data) and traverses the tree to implement `NSXMLParser`-style delegate 
+ *  callbacks.
+ *
+ *  This is useful for implementing custom renderers.
+ *
+ *  @warning This class is not thread-safe and can only be accessed from a single
+ *  thread at a time.
+ */
+@interface CMParser : NSObject
+
+/**
+ *  Initializes the receiver with a document.
+ *
+ *  @param document CommonMark document.
+ *  @param delegate Delegate to receive callbacks during parsing.
+ *
+ *  @return An initialized instance of the receiver.
+ */
+- (instancetype)initWithDocument:(CMDocument *)document delegate:(id<CMParserDelegate>)delegate;
+
+/**
+ *  Document being parsed.
+ */
+@property (nonatomic, readonly) CMDocument *document;
+
+/**
+ *  Delegate to receive callbacks during parsing.
+ */
+@property (nonatomic, weak, readonly) id<CMParserDelegate> delegate;
+
+/**
+ *  Returns the node currently being parsed, or `nil` if not parsing.
+ */
+@property (atomic, readonly) CMNode *currentNode;
+
+/**
+ *  Start parsing.
+ */
+- (void)parse;
+
+/**
+ *  Stop parsing. If implemented, `-parserDidAbort:` will be called on the delegate.
+ */
+- (void)abortParsing;
+
+@end
+
+@protocol CMParserDelegate <NSObject>
+@optional
+- (void)parserDidStartDocument:(CMParser *)parser;
+- (void)parserDidEndDocument:(CMParser *)parser;
+- (void)parserDidAbort:(CMParser *)parser;
+
+- (void)parser:(CMParser *)parser foundText:(NSString *)text;
+- (void)parserFoundHRule:(CMParser *)parser;
+
+- (void)parser:(CMParser *)parser didStartHeaderWithLevel:(NSInteger)level;
+- (void)parser:(CMParser *)parser didEndHeaderWithLevel:(NSInteger)level;
+
+- (void)parserDidStartParagraph:(CMParser *)parser;
+- (void)parserDidEndParagraph:(CMParser *)parser;
+
+- (void)parserDidStartEmphasis:(CMParser *)parser;
+- (void)parserDidEndEmphasis:(CMParser *)parser;
+
+- (void)parserDidStartStrong:(CMParser *)parser;
+- (void)parserDidEndStrong:(CMParser *)parser;
+
+- (void)parser:(CMParser *)parser didStartLinkWithURL:(NSURL *)URL title:(NSString *)title;
+- (void)parser:(CMParser *)parser didEndLinkWithURL:(NSURL *)URL title:(NSString *)title;
+
+- (void)parser:(CMParser *)parser didStartImageWithURL:(NSURL *)URL title:(NSString *)title;
+- (void)parser:(CMParser *)parser didEndImageWithURL:(NSURL *)URL title:(NSString *)title;
+
+- (void)parser:(CMParser *)parser foundHTML:(NSString *)HTML;
+- (void)parser:(CMParser *)parser foundInlineHTML:(NSString *)HTML;
+
+- (void)parser:(CMParser *)parser foundCodeBlock:(NSString *)code info:(NSString *)info;
+- (void)parser:(CMParser *)parser foundInlineCode:(NSString *)code;
+
+- (void)parserFoundSoftBreak:(CMParser *)parser;
+- (void)parserFoundLineBreak:(CMParser *)parser;
+
+- (void)parserDidStartBlockQuote:(CMParser *)parser;
+- (void)parserDidEndBlockQuote:(CMParser *)parser;
+
+- (void)parser:(CMParser *)parser didStartUnorderedListWithTightness:(BOOL)tight;
+- (void)parser:(CMParser *)parser didEndUnorderedListWithTightness:(BOOL)tight;
+
+- (void)parser:(CMParser *)parser didStartOrderedListWithStartingNumber:(NSInteger)num tight:(BOOL)tight;
+- (void)parser:(CMParser *)parser didEndOrderedListWithStartingNumber:(NSInteger)num tight:(BOOL)tight;
+
+- (void)parserDidStartListItem:(CMParser *)parser;
+- (void)parserDidEndListItem:(CMParser *)parser;
+
+@end
+

+ 292 - 0
CocoaMarkdown/CMParser.m

@@ -0,0 +1,292 @@
+//
+//  CMParser.m
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/13/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMParser.h"
+#import "CMDocument.h"
+#import "CMIterator.h"
+#import "CMNode.h"
+
+#import <libkern/OSAtomic.h>
+
+@interface CMParser ()
+@property (atomic, readwrite) CMNode *currentNode;
+@property (nonatomic, weak, readwrite) id<CMParserDelegate> delegate;
+@end
+
+@implementation CMParser {
+    struct {
+        unsigned int didStartDocument:1;
+        unsigned int didEndDocument:1;
+        unsigned int didAbort:1;
+        unsigned int foundText:1;
+        unsigned int foundHRule:1;
+        unsigned int didStartHeader:1;
+        unsigned int didEndHeader:1;
+        unsigned int didStartParagraph:1;
+        unsigned int didEndParagraph:1;
+        unsigned int didStartEmphasis:1;
+        unsigned int didEndEmphasis:1;
+        unsigned int didStartStrong:1;
+        unsigned int didEndStrong:1;
+        unsigned int didStartLink:1;
+        unsigned int didEndLink:1;
+        unsigned int didStartImage:1;
+        unsigned int didEndImage:1;
+        unsigned int foundHTML:1;
+        unsigned int foundInlineHTML:1;
+        unsigned int foundCodeBlock:1;
+        unsigned int foundInlineCode:1;
+        unsigned int foundSoftBreak:1;
+        unsigned int foundLineBreak:1;
+        unsigned int didStartBlockQuote:1;
+        unsigned int didEndBlockQuote:1;
+        unsigned int didStartUnorderedList:1;
+        unsigned int didEndUnorderedList:1;
+        unsigned int didStartOrderedList:1;
+        unsigned int didEndOrderedList:1;
+        unsigned int didStartListItem:1;
+        unsigned int didEndListItem:1;
+    } _delegateFlags;
+    volatile int32_t _parsing;
+}
+
+#pragma mark - Initialization
+
+- (instancetype)initWithDocument:(CMDocument *)document delegate:(id<CMParserDelegate>)delegate
+{
+    NSParameterAssert(document);
+    NSParameterAssert(delegate);
+    
+    if ((self = [super init])) {
+        _document = document;
+        self.delegate = delegate;
+    }
+    return self;
+}
+
+#pragma mark - Parsing
+
+- (void)parse
+{
+    if (!OSAtomicCompareAndSwap32Barrier(0, 1, &_parsing)) return;
+    
+    [[_document.rootNode iterator] enumerateUsingBlock:^(CMNode *node, CMEventType event, BOOL *stop) {
+        self.currentNode = node;
+        [self handleNode:node event:event];
+        if (_parsing == 0) *stop = YES;
+    }];
+    
+    _parsing = 0;
+}
+
+- (void)abortParsing
+{
+    if (!OSAtomicCompareAndSwap32Barrier(1, 0, &_parsing)) return;
+    
+    if (_delegateFlags.didAbort) {
+        [_delegate parserDidAbort:self];
+    }
+}
+
+- (void)handleNode:(CMNode *)node event:(CMEventType)event {
+    NSAssert((event == CMEventTypeEnter) || (event == CMEventTypeExit), @"Event must be either an exit or enter event");
+    
+    switch (node.type) {
+        case CMNodeTypeDocument:
+            if (event == CMEventTypeEnter) {
+                if (_delegateFlags.didStartDocument) {
+                    [_delegate parserDidStartDocument:self];
+                }
+            } else if (_delegateFlags.didEndDocument) {
+                [_delegate parserDidEndDocument:self];
+            }
+            break;
+        case CMNodeTypeText:
+            if (_delegateFlags.foundText) {
+                [_delegate parser:self foundText:node.stringValue];
+            }
+            break;
+        case CMNodeTypeHRule:
+            if (_delegateFlags.foundHRule) {
+                [_delegate parserFoundHRule:self];
+            }
+            break;
+        case CMNodeTypeHeader:
+            if (event == CMEventTypeEnter) {
+                if (_delegateFlags.didStartHeader) {
+                    [_delegate parser:self didStartHeaderWithLevel:node.headerLevel];
+                }
+            } else if (_delegateFlags.didEndHeader) {
+                [_delegate parser:self didEndHeaderWithLevel:node.headerLevel];
+            }
+            break;
+        case CMNodeTypeParagraph:
+            if (event == CMEventTypeEnter) {
+                if (_delegateFlags.didStartParagraph) {
+                    [_delegate parserDidStartParagraph:self];
+                }
+            } else if (_delegateFlags.didEndParagraph) {
+                [_delegate parserDidEndParagraph:self];
+            }
+            break;
+        case CMNodeTypeEmphasis:
+            if (event == CMEventTypeEnter) {
+                if (_delegateFlags.didStartEmphasis) {
+                    [_delegate parserDidStartEmphasis:self];
+                }
+            } else if (_delegateFlags.didEndEmphasis) {
+                [_delegate parserDidEndEmphasis:self];
+            }
+            break;
+        case CMNodeTypeStrong:
+            if (event == CMEventTypeEnter) {
+                if (_delegateFlags.didStartStrong) {
+                    [_delegate parserDidStartStrong:self];
+                }
+            } else if (_delegateFlags.didEndStrong) {
+                [_delegate parserDidEndStrong:self];
+            }
+            break;
+        case CMNodeTypeLink: {
+            NSURL *nodeURL = [_document targetURLForNode:node];
+            if (event == CMEventTypeEnter) {
+                if (_delegateFlags.didStartLink) {
+                    [_delegate parser:self didStartLinkWithURL:nodeURL title:node.title];
+                }
+            } else if (_delegateFlags.didEndLink) {
+                [_delegate parser:self didEndLinkWithURL:nodeURL title:node.title];
+            }
+        }   break;
+        case CMNodeTypeImage: {
+            NSURL *nodeURL = [_document targetURLForNode:node];
+            if (event == CMEventTypeEnter) {
+                if (_delegateFlags.didStartImage) {
+                    [_delegate parser:self didStartImageWithURL:nodeURL title:node.title];
+                }
+            } else if (_delegateFlags.didEndImage) {
+                [_delegate parser:self didEndImageWithURL:nodeURL title:node.title];
+            }
+        }   break;
+        case CMNodeTypeHTML:
+            if (_delegateFlags.foundHTML) {
+                [_delegate parser:self foundHTML:node.stringValue];
+            }
+            break;
+        case CMNodeTypeInlineHTML:
+            if (_delegateFlags.foundInlineHTML) {
+                [_delegate parser:self foundInlineHTML:node.stringValue];
+            }
+            break;
+        case CMNodeTypeCodeBlock:
+            if (_delegateFlags.foundCodeBlock) {
+                [_delegate parser:self foundCodeBlock:node.stringValue info:node.fencedCodeInfo];
+            }
+            break;
+        case CMNodeTypeCode:
+            if (_delegateFlags.foundInlineCode) {
+                [_delegate parser:self foundInlineCode:node.stringValue];
+            }
+            break;
+        case CMNodeTypeSoftbreak:
+            if (_delegateFlags.foundSoftBreak) {
+                [_delegate parserFoundSoftBreak:self];
+            }
+            break;
+        case CMNodeTypeLinebreak:
+            if (_delegateFlags.foundLineBreak) {
+                [_delegate parserFoundLineBreak:self];
+            }
+            break;
+        case CMNodeTypeBlockQuote:
+            if (event == CMEventTypeEnter) {
+                if (_delegateFlags.didStartBlockQuote) {
+                    [_delegate parserDidStartBlockQuote:self];
+                }
+            } else if (_delegateFlags.didEndBlockQuote) {
+                [_delegate parserDidEndBlockQuote:self];
+            }
+            break;
+        case CMNodeTypeList:
+            switch (node.listType) {
+                case CMListTypeOrdered:
+                    if (event == CMEventTypeEnter) {
+                        if (_delegateFlags.didStartOrderedList) {
+                            [_delegate parser:self didStartOrderedListWithStartingNumber:node.listStartingNumber tight:node.listTight];
+                        }
+                    } else if (_delegateFlags.didEndOrderedList) {
+                        [_delegate parser:self didEndOrderedListWithStartingNumber:node.listStartingNumber tight:node.listTight];
+                    }
+                    break;
+                case CMListTypeUnordered:
+                    if (event == CMEventTypeEnter) {
+                        if (_delegateFlags.didStartUnorderedList) {
+                            [_delegate parser:self didStartUnorderedListWithTightness:node.listTight];
+                        }
+                    } else if (_delegateFlags.didEndUnorderedList) {
+                        [_delegate parser:self didEndUnorderedListWithTightness:node.listTight];
+                    }
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case CMNodeTypeItem:
+            if (event == CMEventTypeEnter) {
+                if (_delegateFlags.didStartListItem) {
+                    [_delegate parserDidStartListItem:self];
+                }
+            } else if (_delegateFlags.didEndListItem) {
+                [_delegate parserDidEndListItem:self];
+            }
+            break;
+        default:
+            break;
+    }
+}
+
+#pragma mark - Accessors
+
+- (void)setDelegate:(id<CMParserDelegate>)delegate
+{
+    if (_delegate != delegate) {
+        _delegate = delegate;
+        _delegateFlags.didStartDocument = [_delegate respondsToSelector:@selector(parserDidStartDocument:)];
+        _delegateFlags.didEndDocument = [_delegate respondsToSelector:@selector(parserDidEndDocument:)];
+        _delegateFlags.didAbort = [_delegate respondsToSelector:@selector(parserDidAbort:)];
+        _delegateFlags.foundText = [_delegate respondsToSelector:@selector(parser:foundText:)];
+        _delegateFlags.foundHRule = [_delegate respondsToSelector:@selector(parserFoundHRule:)];
+        _delegateFlags.didStartHeader = [_delegate respondsToSelector:@selector(parser:didStartHeaderWithLevel:)];
+        _delegateFlags.didEndHeader = [_delegate respondsToSelector:@selector(parser:didEndHeaderWithLevel:)];
+        _delegateFlags.didStartParagraph = [_delegate respondsToSelector:@selector(parserDidStartParagraph:)];
+        _delegateFlags.didEndParagraph = [_delegate respondsToSelector:@selector(parserDidEndParagraph:)];
+        _delegateFlags.didStartEmphasis = [_delegate respondsToSelector:@selector(parserDidStartEmphasis:)];
+        _delegateFlags.didEndEmphasis = [_delegate respondsToSelector:@selector(parserDidEndEmphasis:)];
+        _delegateFlags.didStartStrong = [_delegate respondsToSelector:@selector(parserDidStartStrong:)];
+        _delegateFlags.didEndStrong = [_delegate respondsToSelector:@selector(parserDidEndStrong:)];
+        _delegateFlags.didStartLink = [_delegate respondsToSelector:@selector(parser:didStartLinkWithURL:title:)];
+        _delegateFlags.didEndLink = [_delegate respondsToSelector:@selector(parser:didEndLinkWithURL:title:)];
+        _delegateFlags.didStartImage = [_delegate respondsToSelector:@selector(parser:didStartImageWithURL:title:)];
+        _delegateFlags.didEndImage = [_delegate respondsToSelector:@selector(parser:didEndImageWithURL:title:)];
+        _delegateFlags.foundHTML = [_delegate respondsToSelector:@selector(parser:foundHTML:)];
+        _delegateFlags.foundInlineHTML = [_delegate respondsToSelector:@selector(parser:foundInlineHTML:)];
+        _delegateFlags.foundCodeBlock = [_delegate respondsToSelector:@selector(parser:foundCodeBlock:info:)];
+        _delegateFlags.foundInlineCode = [_delegate respondsToSelector:@selector(parser:foundInlineCode:)];
+        _delegateFlags.foundSoftBreak = [_delegate respondsToSelector:@selector(parserFoundSoftBreak:)];
+        _delegateFlags.foundLineBreak = [_delegate respondsToSelector:@selector(parserFoundLineBreak:)];
+        _delegateFlags.didStartBlockQuote = [_delegate respondsToSelector:@selector(parserDidStartBlockQuote:)];
+        _delegateFlags.didEndBlockQuote = [_delegate respondsToSelector:@selector(parserDidEndBlockQuote:)];
+        _delegateFlags.didStartUnorderedList = [_delegate respondsToSelector:@selector(parser:didStartUnorderedListWithTightness:)];
+        _delegateFlags.didEndUnorderedList = [_delegate respondsToSelector:@selector(parser:didEndUnorderedListWithTightness:)];
+        _delegateFlags.didStartOrderedList = [_delegate respondsToSelector:@selector(parser:didStartOrderedListWithStartingNumber:tight:)];
+        _delegateFlags.didEndOrderedList = [_delegate respondsToSelector:@selector(parser:didEndOrderedListWithStartingNumber:tight:)];
+        _delegateFlags.didStartListItem = [_delegate respondsToSelector:@selector(parserDidStartListItem:)];
+        _delegateFlags.didEndListItem = [_delegate respondsToSelector:@selector(parserDidEndListItem:)];
+    }
+}
+
+@end

+ 44 - 0
CocoaMarkdown/CMPlatformDefines.h

@@ -0,0 +1,44 @@
+//
+//  CMPlatformDefines.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/15/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#ifndef CocoaMarkdown_CMPlatformDefines_h
+#define CocoaMarkdown_CMPlatformDefines_h
+
+#if TARGET_OS_IPHONE
+#import <UIKit/UIKit.h>
+
+#define CMColor UIColor
+#define CMFontSymbolicTraits UIFontDescriptorSymbolicTraits
+#define CMFont UIFont
+#define CMFontDescriptor UIFontDescriptor
+#define CMFontTraitsAttribute UIFontDescriptorTraitsAttribute
+#define CMFontTraitItalic UIFontDescriptorTraitItalic
+#define CMFontTraitBold UIFontDescriptorTraitBold
+#define CMUnderlineStyle NSUnderlineStyle
+
+typedef UIFontDescriptorAttributeName CMFontDescriptorAttributeName;
+typedef UIFontDescriptorTraitKey CMFontDescriptorTraitKey;
+
+#else
+#import <Cocoa/Cocoa.h>
+
+#define CMColor NSColor
+#define CMFontSymbolicTraits NSFontSymbolicTraits
+#define CMFont NSFont
+#define CMFontDescriptor NSFontDescriptor
+#define CMFontTraitsAttribute NSFontTraitsAttribute
+#define CMFontTraitItalic NSFontItalicTrait
+#define CMFontTraitBold NSFontBoldTrait
+#define CMUnderlineStyle NSInteger
+
+typedef NSFontDescriptorAttributeName CMFontDescriptorAttributeName;
+typedef NSFontDescriptorTraitKey CMFontDescriptorTraitKey;
+
+#endif
+
+#endif

+ 20 - 0
CocoaMarkdown/CMStack.h

@@ -0,0 +1,20 @@
+//
+//  CMStack.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/16/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+/**
+ *  Array backed stack.
+ */
+@interface CMStack<ElementType> : NSObject
+@property (nonatomic, readonly) NSArray<ElementType> *objects;
+
+- (void)push:(ElementType)object;
+- (ElementType)pop;
+- (ElementType)peek;
+@end

+ 41 - 0
CocoaMarkdown/CMStack.m

@@ -0,0 +1,41 @@
+
+//
+//  CMStack.m
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/16/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMStack.h"
+
+@implementation CMStack {
+    NSMutableArray *_objects;
+}
+
+- (instancetype)init
+{
+    if ((self = [super init])) {
+        _objects = [[NSMutableArray alloc] init];
+    }
+    return self;
+}
+
+- (void)push:(id)object
+{
+    [_objects addObject:object];
+}
+
+- (id)pop
+{
+    id object = _objects.lastObject;
+    [_objects removeLastObject];
+    return object;
+}
+
+- (id)peek
+{
+    return _objects.lastObject;
+}
+
+@end

+ 296 - 0
CocoaMarkdown/CMTextAttributes.h

@@ -0,0 +1,296 @@
+//
+//  CMTextAttributes.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/15/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+#import "CMPlatformDefines.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef NS_OPTIONS(NSUInteger, CMElementKind) {
+    CMElementKindText = 1 << 0,
+    
+    CMElementKindHeader1 = 1 << 1,
+    CMElementKindHeader2 = 1 << 2,
+    CMElementKindHeader3 = 1 << 3,
+    CMElementKindHeader4 = 1 << 4,
+    CMElementKindHeader5 = 1 << 5,
+    CMElementKindHeader6 = 1 << 6,
+    CMElementKindAnyHeader = CMElementKindHeader1 | CMElementKindHeader2 | CMElementKindHeader3 | CMElementKindHeader4 | CMElementKindHeader5 | CMElementKindHeader6,
+    
+    CMElementKindParagraph = 1 << 7,
+    CMElementKindLink = 1 << 8,
+    CMElementKindImageParagraph = 1 << 9,
+    CMElementKindCodeBlock = 1 << 10,
+    CMElementKindInlineCode = 1 << 11,
+    CMElementKindBlockQuote = 1 << 12,
+    
+    CMElementKindOrderedList = 1 << 13,
+    CMElementKindOrderedSublist = 1 << 14,
+    CMElementKindOrderedListItem = 1 << 15,
+    CMElementKindUnorderedList = 1 << 16,
+    CMElementKindUnorderedSublist = 1 << 17,
+    CMElementKindUnorderedListItem = 1 << 18,
+};
+
+@class CMStyleAttributes;
+
+typedef NSString * CMParagraphStyleAttributeName NS_EXTENSIBLE_STRING_ENUM;
+
+/**
+ *  Container for sets of text attributes used to style 
+ *  attributed strings.
+ */
+@interface CMTextAttributes : NSObject
+
+/**
+ *  Initializes the receiver with the default attributes.
+ *
+ *  @return An initialized instance of the receiver.
+ */
+- (instancetype)init;
+
+/// Set additional attributes for one or more element-kind
+/// 
+/// @param attributes A dictionary of string attributes that will be added to existing attributes for every specified element kind
+/// @param elementKind The mask of target element kinds
+///
+- (void) addStringAttributes:(NSDictionary<NSAttributedStringKey, id>*)attributes forElementWithKinds:(CMElementKind)elementKinds;
+
+/// Set additional font attributes for one or more element-kind
+/// 
+/// @param attributes A dictionary of font-descriptor attributes that will be added to existing attributes for every specified element kind
+/// @param elementKind The mask of target element kinds
+///
+- (void) addFontAttributes:(NSDictionary<CMFontDescriptorAttributeName, id>*)fontAttributes forElementWithKinds:(CMElementKind)elementKinds;
+
+/// Set font traits for one or more element-kind
+/// 
+/// @param fontTraits A dictionary of font-trait attributes that will be set for every specified element kind
+/// @param elementKind The mask of target element kinds
+/// @description This is a specialized version of `addFontAttributes:forElementWithKinds:` dedicated to the font-trait attribute
+///
+- (void) setFontTraits:(NSDictionary<CMFontDescriptorTraitKey, id>*)fontTraits forElementWithKinds:(CMElementKind)elementKinds;
+
+/// Set additional paragraph attributes for one or more element-kind
+/// 
+/// @param attributes A dictionary of string attributes that will be added to existing attributes for every specified element kind
+/// @param elementKind The mask of target element kinds
+///
+- (void) addParagraphStyleAttributes:(NSDictionary<CMParagraphStyleAttributeName, id>*)attributes forElementWithKinds:(CMElementKind)elementKinds;
+
+/**
+ *  @param level The header level.
+ *
+ *  @return The attributes for the specified header level.
+ */
+- (CMStyleAttributes *)attributesForHeaderLevel:(NSInteger)level;
+
+/**
+ *  Attributes used to style text.
+ *
+ *  On iOS, defaults to using the Dynamic Type font with style `UIFontTextStyleBody`
+ *  On OS X, defaults to using the user font with size 12pt.
+ */
+@property (nonatomic) CMStyleAttributes *baseTextAttributes;
+
+/**
+ *  Attributes used to style level 1 headers.
+ *
+ *  On iOS, defaults to using the Dynamic Type font with style `UIFontTextStyleHeadline`
+ *  On OS X, defaults to using the user font with size 24pt.
+ */
+@property (nonatomic) CMStyleAttributes *h1Attributes;
+
+/**
+ *  Attributes used to style level 2 headers.
+ *
+ *  On iOS, defaults to using the Dynamic Type font with style `UIFontTextStyleHeadline`
+ *  On OS X, defaults to using the user font with size 18pt.
+ */
+@property (nonatomic) CMStyleAttributes *h2Attributes;
+
+/**
+ *  Attributes used to style level 3 headers.
+ *
+ *  On iOS, defaults to using the Dynamic Type font with style `UIFontTextStyleHeadline`
+ *  On OS X, defaults to using the user font with size 14pt.
+ */
+@property (nonatomic) CMStyleAttributes *h3Attributes;
+
+/**
+ *  Attributes used to style level 4 headers.
+ *
+ *  On iOS, defaults to using the Dynamic Type font with style `UIFontTextStyleSubheadline`
+ *  On OS X, defaults to using the user font with size 12pt.
+ */
+@property (nonatomic) CMStyleAttributes *h4Attributes;
+
+/**
+ *  Attributes used to style level 5 headers.
+ *
+ *  On iOS, defaults to using the Dynamic Type font with style `UIFontTextStyleSubheadline`
+ *  On OS X, defaults to using the user font with size 10pt.
+ */
+@property (nonatomic) CMStyleAttributes *h5Attributes;
+
+/**
+ *  Attributes used to style level 6 headers.
+ *
+ *  On iOS, defaults to using the Dynamic Type font with style `UIFontTextStyleSubheadline`
+ *  On OS X, defaults to using the user font with size 8pt.
+ */
+@property (nonatomic) CMStyleAttributes *h6Attributes;
+
+/**
+ *  Attributes used to style paragraphs.
+ *
+ *  Defaults to using a 12pt paragraph spacing
+ */
+@property (nonatomic) CMStyleAttributes *paragraphAttributes;
+
+/**
+ *  Attributes used to style emphasized text.
+ *
+ *  If not set, the renderer will attempt to infer the emphasized font from the
+ *  regular text font.
+ */
+@property (nonatomic) CMStyleAttributes *emphasisAttributes;
+
+/**
+ *  Attributes used to style strong text.
+ *
+ *  If not set, the renderer will attempt to infer the strong font from the
+ *  regular text font.
+ */
+@property (nonatomic) CMStyleAttributes *strongAttributes;
+
+/**
+ *  Attributes used to style linked text.
+ *
+ *  Defaults to using a blue foreground color and a single line underline style.
+ */
+@property (nonatomic) CMStyleAttributes *linkAttributes;
+
+
+/**
+ *  Attributes used to style images paragraphs.
+ *
+ *  Defaults to centering the image.
+ */
+@property (nonatomic) CMStyleAttributes *imageParagraphAttributes;
+
+/**
+ *  Attributes used to style code blocks.
+ *
+ *  On iOS, defaults to the Menlo font when available, or Courier as a fallback.
+ *  On OS X, defaults to the user monospaced font.
+ */
+@property (nonatomic) CMStyleAttributes *codeBlockAttributes;
+
+/**
+ *  Attributes used to style inline code.
+ *
+ *  On iOS, defaults to the Menlo font when available, or Courier as a fallback.
+ *  On OS X, defaults to the user monospaced font.
+ */
+@property (nonatomic) CMStyleAttributes *inlineCodeAttributes;
+
+/**
+ *  Attributes used to style block quotes.
+ *
+ *  Defaults to using a paragraph style with a head indent of 30px.
+ */
+@property (nonatomic) CMStyleAttributes *blockQuoteAttributes;
+
+/**
+ *  Attributes used to style ordered lists.
+ *
+ *  These attributes will apply to the entire list (unless overriden by attributes
+ *  for the list items), including the numbers.
+ *
+ *  Defaults to using a paragraph style with a head indent of 30px.
+ */
+@property (nonatomic) CMStyleAttributes *orderedListAttributes;
+
+/**
+ *  Attributes used to style unordered lists.
+ *
+ *  These attributes will apply to the entire list (unless overriden by attributes
+ *  for the list items), including the bullets.
+ *
+ *  Defaults to using a paragraph style with a head indent of 30px.
+ */
+@property (nonatomic) CMStyleAttributes *unorderedListAttributes;
+
+/**
+ *  Attributes used to style ordered sublists.
+ *
+ *  These attributes will apply to the entire list (unless overriden by attributes
+ *  for the list items), including the numbers.
+ *
+ *  Defaults to using a paragraph style with a head indent of 30px.
+ */
+@property (nonatomic) CMStyleAttributes *orderedSublistAttributes;
+
+/**
+ *  Attributes used to style unordered sublists.
+ *
+ *  These attributes will apply to the entire list (unless overriden by attributes
+ *  for the list items), including the bullets.
+ *
+ *  Defaults to using a paragraph style with a head indent of 30px.
+ */
+@property (nonatomic) CMStyleAttributes *unorderedSublistAttributes;
+
+/**
+ *  Attributes used to style ordered list items.
+ *
+ *  These attribtues do _not_ apply to the numbers.
+ */
+@property (nonatomic) CMStyleAttributes *orderedListItemAttributes;
+
+/**
+ *  Attributes used to style unordered list items.
+ *
+ *  These attribtues do _not_ apply to the bullets.
+ */
+@property (nonatomic) CMStyleAttributes *unorderedListItemAttributes;
+
+@end
+
+
+@interface CMStyleAttributes: NSObject <NSCopying>
+
+@property (readonly) NSMutableDictionary<NSAttributedStringKey, id> * stringAttributes;
+@property (readonly) NSMutableDictionary<CMFontDescriptorAttributeName, id> * fontAttributes;
+@property (readonly) NSMutableDictionary<CMParagraphStyleAttributeName, id> * paragraphStyleAttributes;
+
+// Helper method for setting specific symbolic traits in fontAttributes
+- (void) setFontSymbolicTraits:(CMFontSymbolicTraits)fontSymbolicTraits;
+
+@end
+
+
+extern CMParagraphStyleAttributeName const CMParagraphStyleAttributeLineSpacing;
+extern CMParagraphStyleAttributeName const CMParagraphStyleAttributeParagraphSpacing;
+extern CMParagraphStyleAttributeName const CMParagraphStyleAttributeAlignment;
+extern CMParagraphStyleAttributeName const CMParagraphStyleAttributeFirstLineHeadExtraIndent;
+extern CMParagraphStyleAttributeName const CMParagraphStyleAttributeHeadExtraIndent;
+extern CMParagraphStyleAttributeName const CMParagraphStyleAttributeTailExtraIndent;
+extern CMParagraphStyleAttributeName const CMParagraphStyleAttributeLineBreakMode;
+extern CMParagraphStyleAttributeName const CMParagraphStyleAttributeMinimumLineHeight;
+extern CMParagraphStyleAttributeName const CMParagraphStyleAttributeMaximumLineHeight;
+extern CMParagraphStyleAttributeName const CMParagraphStyleAttributeLineHeightMultiple;
+extern CMParagraphStyleAttributeName const CMParagraphStyleAttributeParagraphSpacingBefore;
+extern CMParagraphStyleAttributeName const CMParagraphStyleAttributeHyphenationFactor;
+extern CMParagraphStyleAttributeName const CMParagraphStyleAttributeListItemLabelIndent;
+extern CMParagraphStyleAttributeName const CMParagraphStyleAttributeListItemBulletString;
+extern CMParagraphStyleAttributeName const CMParagraphStyleAttributeListItemNumberFormat;
+
+NS_ASSUME_NONNULL_END

+ 458 - 0
CocoaMarkdown/CMTextAttributes.m

@@ -0,0 +1,458 @@
+//
+//  CMTextAttributes.m
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/15/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMTextAttributes.h"
+#import "CMPlatformDefines.h"
+
+@interface CMStyleAttributes ()
+
+- (instancetype) initWithStringAttributes:(NSDictionary<NSAttributedStringKey, id>*) textAttributes;
+- (instancetype) initWithStringAttributes:(NSDictionary<NSAttributedStringKey, id>*) textAttributes paragraphStyleAttributes:(NSDictionary<CMParagraphStyleAttributeName, id>*)paragraphStyleAttributes;
+- (instancetype) initWithParagraphStyleAttributes:(NSDictionary<CMParagraphStyleAttributeName, id>*)paragraphStyleAttributes;
+- (instancetype) initWithFontSymbolicTraits:(CMFontSymbolicTraits)fontSymbolicTraits;
+
+@end
+
+static CMStyleAttributes * CMDefaultBaseTextAttributes()
+{
+    NSDictionary* stringAttributes;
+#if TARGET_OS_IPHONE
+    stringAttributes = @{NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleBody]};
+#else
+    stringAttributes = @{NSFontAttributeName: [NSFont userFontOfSize:12.0]};
+#endif
+    return [[CMStyleAttributes alloc] initWithStringAttributes:stringAttributes];
+}
+
+static NSDictionary<CMParagraphStyleAttributeName, id> * defaultHeaderParagraphStyleAttributes()
+{
+    return @{ CMParagraphStyleAttributeParagraphSpacingBefore: @16,
+              CMParagraphStyleAttributeParagraphSpacing: @8 };
+}
+
+static CMStyleAttributes * CMDefaultH1Attributes()
+{
+    NSDictionary* stringAttributes;
+#if TARGET_OS_IPHONE
+    stringAttributes = @{ NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleTitle1] };
+#else
+    stringAttributes = @{ NSFontAttributeName: [NSFont boldSystemFontOfSize:28.0] };
+#endif
+    return [[CMStyleAttributes alloc] initWithStringAttributes:stringAttributes 
+                                      paragraphStyleAttributes:@{ CMParagraphStyleAttributeParagraphSpacingBefore: @28,
+                                                                  CMParagraphStyleAttributeParagraphSpacing: @14 }];
+}
+
+static CMStyleAttributes * CMDefaultH2Attributes()
+{
+    NSDictionary* stringAttributes;
+    #if TARGET_OS_IPHONE
+        stringAttributes = @{ NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleTitle2] };
+    #else
+        stringAttributes = @{ NSFontAttributeName: [NSFont boldSystemFontOfSize:22.0] };
+    #endif
+    return [[CMStyleAttributes alloc] initWithStringAttributes:stringAttributes 
+                                      paragraphStyleAttributes:@{ CMParagraphStyleAttributeParagraphSpacingBefore: @20,
+                                                                  CMParagraphStyleAttributeParagraphSpacing: @10 }];
+}
+
+static CMStyleAttributes * CMDefaultH3Attributes()
+{
+    NSDictionary* stringAttributes;
+#if TARGET_OS_IPHONE
+    stringAttributes = @{ NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleTitle3] };
+#else
+    stringAttributes = @{ NSFontAttributeName: [NSFont boldSystemFontOfSize:16.0] };
+#endif
+    return [[CMStyleAttributes alloc] initWithStringAttributes:stringAttributes 
+                                      paragraphStyleAttributes:defaultHeaderParagraphStyleAttributes()];
+}
+
+static CMStyleAttributes * CMDefaultH4Attributes()
+{
+    NSDictionary* stringAttributes;
+#if TARGET_OS_IPHONE
+    stringAttributes = @{ NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline] };
+#else
+    stringAttributes = @{ NSFontAttributeName: [NSFont boldSystemFontOfSize:14.0] };
+#endif
+    return [[CMStyleAttributes alloc] initWithStringAttributes:stringAttributes 
+                                      paragraphStyleAttributes:defaultHeaderParagraphStyleAttributes()];
+}
+
+static CMStyleAttributes * CMDefaultH5Attributes()
+{
+    NSDictionary* stringAttributes;
+#if TARGET_OS_IPHONE
+    stringAttributes = @{ NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline] };
+#else
+    stringAttributes = @{ NSFontAttributeName: [NSFont boldSystemFontOfSize:12.0] };
+#endif
+    return [[CMStyleAttributes alloc] initWithStringAttributes:stringAttributes 
+                                      paragraphStyleAttributes:defaultHeaderParagraphStyleAttributes()];
+}
+
+static CMStyleAttributes * CMDefaultH6Attributes()
+{
+    NSDictionary* stringAttributes;
+#if TARGET_OS_IPHONE
+    stringAttributes = @{ NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline] };
+#else
+    stringAttributes = @{ NSFontAttributeName: [NSFont boldSystemFontOfSize:10.0] };
+#endif
+    return [[CMStyleAttributes alloc] initWithStringAttributes:stringAttributes 
+                                      paragraphStyleAttributes:defaultHeaderParagraphStyleAttributes()];
+}
+
+static CMStyleAttributes * CMDefaultParagraphAttributes()
+{
+    return [[CMStyleAttributes alloc] initWithParagraphStyleAttributes:@{ CMParagraphStyleAttributeParagraphSpacingBefore: @12 }];
+}
+
+static CMStyleAttributes * CMDefaultLinkAttributes()
+{
+    return [[CMStyleAttributes alloc] initWithStringAttributes:@{ NSForegroundColorAttributeName: CMColor.blueColor,
+                                                                  NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle) }];
+}
+
+static CMStyleAttributes * CMDefaultImageParagraphAttributes()
+{
+    return [[CMStyleAttributes alloc] initWithParagraphStyleAttributes:@{ CMParagraphStyleAttributeParagraphSpacingBefore: @12,
+                                                                          CMParagraphStyleAttributeAlignment: @(NSTextAlignmentCenter) }];
+}
+
+#if TARGET_OS_IPHONE
+static UIFont * defaultMonospaceFont()
+{
+    if (@available(iOS 11.0, *)) {
+        CGFloat baseFontSize = [UIFont preferredFontForTextStyle:UIFontTextStyleBody 
+                                   compatibleWithTraitCollection:[UITraitCollection traitCollectionWithPreferredContentSizeCategory:UIContentSizeCategoryMedium]].pointSize;
+        UIFont* baseMonospaceFont = [UIFont fontWithName:@"Menlo" size:baseFontSize] ?: [UIFont fontWithName:@"Courier" size:baseFontSize];
+        return [[UIFontMetrics metricsForTextStyle:UIFontTextStyleBody] scaledFontForFont:baseMonospaceFont];
+    } else {
+        // Fallback on earlier versions
+        CGFloat size = [[UIFont preferredFontForTextStyle:UIFontTextStyleBody] pointSize];
+        return [UIFont fontWithName:@"Menlo" size:size] ?: [UIFont fontWithName:@"Courier" size:size];
+    }
+}
+#endif
+
+static CGFloat defaultIndentationStep = 30.0;
+
+static CMStyleAttributes * CMDefaultCodeBlockAttributes()
+{
+    NSDictionary* stringAttributes;
+#if TARGET_OS_IPHONE
+    stringAttributes = @{ NSFontAttributeName: defaultMonospaceFont(), };
+#else
+    stringAttributes = @{ NSFontAttributeName: [NSFont userFixedPitchFontOfSize:12.0] };
+#endif
+
+    NSDictionary* paragraphStyleAttributes = @{ CMParagraphStyleAttributeParagraphSpacingBefore: @12.0,
+                                                CMParagraphStyleAttributeFirstLineHeadExtraIndent: @(defaultIndentationStep),
+                                                CMParagraphStyleAttributeHeadExtraIndent: @(defaultIndentationStep) };
+    
+    return [[CMStyleAttributes alloc] initWithStringAttributes:stringAttributes 
+                                      paragraphStyleAttributes:paragraphStyleAttributes];
+}
+
+static CMStyleAttributes * CMDefaultInlineCodeAttributes()
+{
+    NSDictionary* stringAttributes;
+#if TARGET_OS_IPHONE
+    stringAttributes = @{ NSFontAttributeName: defaultMonospaceFont(), };
+#else
+    stringAttributes = @{ NSFontAttributeName: [NSFont userFixedPitchFontOfSize:12.0] };
+#endif
+    return [[CMStyleAttributes alloc] initWithStringAttributes:stringAttributes];
+}
+
+static  CMStyleAttributes *  CMDefaultBlockQuoteAttributes()
+{
+    return [[CMStyleAttributes alloc] initWithParagraphStyleAttributes:@{ CMParagraphStyleAttributeFirstLineHeadExtraIndent: @(defaultIndentationStep),
+                                                                          CMParagraphStyleAttributeHeadExtraIndent: @(defaultIndentationStep) }];
+}
+
+static CGFloat itemLabelIndent = 20.0;
+
+static NSDictionary<CMParagraphStyleAttributeName, id> * DefaultListParagraphStyleAttributes()
+{
+    return @{ CMParagraphStyleAttributeFirstLineHeadExtraIndent: @(defaultIndentationStep + itemLabelIndent),
+              CMParagraphStyleAttributeHeadExtraIndent: @(defaultIndentationStep + itemLabelIndent),
+              CMParagraphStyleAttributeListItemLabelIndent: @(itemLabelIndent),
+              CMParagraphStyleAttributeListItemBulletString: @"●",
+              CMParagraphStyleAttributeListItemNumberFormat: @"%ld.",
+    };
+}
+
+static  CMStyleAttributes *  CMDefaultOrderedListAttributes()
+{
+    return [[CMStyleAttributes alloc] initWithParagraphStyleAttributes:DefaultListParagraphStyleAttributes()];
+}
+
+static  CMStyleAttributes *  CMDefaultUnorderedListAttributes()
+{
+    return [[CMStyleAttributes alloc] initWithParagraphStyleAttributes:DefaultListParagraphStyleAttributes()];
+}
+
+static NSDictionary<CMParagraphStyleAttributeName, id> * DefaultSublistParagraphStyleAttributes()
+{
+    return @{ CMParagraphStyleAttributeFirstLineHeadExtraIndent: @(itemLabelIndent), // Align label with parent list content
+              CMParagraphStyleAttributeHeadExtraIndent: @(itemLabelIndent),
+              CMParagraphStyleAttributeListItemLabelIndent: @(itemLabelIndent),
+              CMParagraphStyleAttributeListItemBulletString: @"○",
+              CMParagraphStyleAttributeListItemNumberFormat: @"%ld.",
+    };
+}
+
+static  CMStyleAttributes *  CMDefaultOrderedSublistAttributes()
+{
+    return [[CMStyleAttributes alloc] initWithParagraphStyleAttributes:DefaultSublistParagraphStyleAttributes()];
+}
+
+static  CMStyleAttributes *  CMDefaultUnorderedSublistAttributes()
+{
+    return [[CMStyleAttributes alloc] initWithParagraphStyleAttributes:DefaultSublistParagraphStyleAttributes()];
+}
+
+@implementation CMTextAttributes
+
+- (instancetype)init
+{
+    if ((self = [super init])) {
+        _baseTextAttributes = CMDefaultBaseTextAttributes();
+        _h1Attributes = CMDefaultH1Attributes();
+        _h2Attributes = CMDefaultH2Attributes();
+        _h3Attributes = CMDefaultH3Attributes();
+        _h4Attributes = CMDefaultH4Attributes();
+        _h5Attributes = CMDefaultH5Attributes();
+        _h6Attributes = CMDefaultH6Attributes();
+        _paragraphAttributes = CMDefaultParagraphAttributes();
+        
+        _emphasisAttributes = [[CMStyleAttributes alloc] initWithFontSymbolicTraits:CMFontTraitItalic];
+        _strongAttributes = [[CMStyleAttributes alloc] initWithFontSymbolicTraits:CMFontTraitBold];
+        
+        _linkAttributes = CMDefaultLinkAttributes();
+        _imageParagraphAttributes = CMDefaultImageParagraphAttributes();
+        _codeBlockAttributes = CMDefaultCodeBlockAttributes();
+        _inlineCodeAttributes = CMDefaultInlineCodeAttributes();
+        _blockQuoteAttributes = CMDefaultBlockQuoteAttributes();
+        _orderedListAttributes = CMDefaultOrderedListAttributes();
+        _unorderedListAttributes = CMDefaultUnorderedListAttributes();
+        _orderedSublistAttributes = CMDefaultOrderedSublistAttributes();
+        _unorderedSublistAttributes = CMDefaultUnorderedSublistAttributes();
+    }
+    return self;
+}
+
+- (void) updateAttributesForElementKinds:(CMElementKind)elementKinds usingBlock:(void(^)(CMStyleAttributes * styleAttributes))block
+{
+    if ((elementKinds & CMElementKindText) != 0) {
+        block(_baseTextAttributes);
+    }
+    if ((elementKinds & CMElementKindHeader1) != 0) {
+        block(_h1Attributes);
+    }
+    if ((elementKinds & CMElementKindHeader2) != 0) {
+        block(_h2Attributes);
+    }
+    if ((elementKinds & CMElementKindHeader3) != 0) {
+        block(_h3Attributes);
+    }
+    if ((elementKinds & CMElementKindHeader4) != 0) {
+        block(_h4Attributes);
+    }
+    if ((elementKinds & CMElementKindHeader5) != 0) {
+        block(_h5Attributes);
+    }
+    if ((elementKinds & CMElementKindHeader6) != 0) {
+        block(_h6Attributes);
+    }
+    if ((elementKinds & CMElementKindParagraph) != 0) {
+        block(_paragraphAttributes);
+    }
+    if ((elementKinds & CMElementKindLink) != 0) {
+        block(_linkAttributes);
+    }
+    if ((elementKinds & CMElementKindImageParagraph) != 0) {
+        block(_imageParagraphAttributes);
+    }
+    if ((elementKinds & CMElementKindCodeBlock) != 0) {
+        block(_codeBlockAttributes);
+    }
+    if ((elementKinds & CMElementKindInlineCode) != 0) {
+        block(_inlineCodeAttributes);
+    }
+    if ((elementKinds & CMElementKindBlockQuote) != 0) {
+        block(_blockQuoteAttributes);
+    }
+    if ((elementKinds & CMElementKindOrderedList) != 0) {
+        block(_orderedListAttributes);
+    }
+    if ((elementKinds & CMElementKindOrderedSublist) != 0) {
+        block(_orderedSublistAttributes);
+    }
+    if ((elementKinds & CMElementKindOrderedListItem) != 0) {
+        block(_orderedListItemAttributes);
+    }
+    if ((elementKinds & CMElementKindUnorderedList) != 0) {
+        block(_unorderedListAttributes);
+    }
+    if ((elementKinds & CMElementKindUnorderedSublist) != 0) {
+        block(_unorderedSublistAttributes);
+    }
+    if ((elementKinds & CMElementKindUnorderedListItem) != 0) {
+        block(_unorderedListItemAttributes);
+    }
+}
+
+- (void) addStringAttributes:(NSDictionary<NSAttributedStringKey, id>*)attributes forElementWithKinds:(CMElementKind)elementKinds
+{
+    [self updateAttributesForElementKinds:elementKinds usingBlock:^(CMStyleAttributes *styleAttributes) {
+        [styleAttributes.stringAttributes addEntriesFromDictionary:attributes];
+    }];
+}
+
+- (void) addFontAttributes:(NSDictionary<CMFontDescriptorAttributeName, id>*)fontAttributes forElementWithKinds:(CMElementKind)elementKinds
+{
+    [self updateAttributesForElementKinds:elementKinds usingBlock:^(CMStyleAttributes *styleAttributes) {
+        [styleAttributes.fontAttributes addEntriesFromDictionary:fontAttributes];
+    }];
+}
+
+- (void) setFontTraits:(NSDictionary<CMFontDescriptorTraitKey, id>*)fontTraits forElementWithKinds:(CMElementKind)elementKinds
+{
+    [self updateAttributesForElementKinds:elementKinds usingBlock:^(CMStyleAttributes *styleAttributes) {
+        styleAttributes.fontAttributes[CMFontTraitsAttribute] = fontTraits;
+    }];
+}
+
+- (void) addParagraphStyleAttributes:(NSDictionary<CMParagraphStyleAttributeName, id>*)attributes forElementWithKinds:(CMElementKind)elementKinds
+{
+    [self updateAttributesForElementKinds:elementKinds usingBlock:^(CMStyleAttributes *styleAttributes) {
+        [styleAttributes.paragraphStyleAttributes addEntriesFromDictionary:attributes];
+    }];
+}
+
+- (CMStyleAttributes *)attributesForHeaderLevel:(NSInteger)level
+{
+    switch (level) {
+        case 1: return _h1Attributes;
+        case 2: return _h2Attributes;
+        case 3: return _h3Attributes;
+        case 4: return _h4Attributes;
+        case 5: return _h5Attributes;
+        default: return _h6Attributes;
+    }
+}
+
+@end
+
+
+@implementation CMStyleAttributes
+
+- (instancetype) init
+{
+    self = [super init];
+    if (self != nil) {
+        _stringAttributes = [NSMutableDictionary new];
+        _fontAttributes = [NSMutableDictionary new];
+        _paragraphStyleAttributes =[NSMutableDictionary new];
+    }
+    return self;
+}
+
+- (instancetype) initWithStringAttributes:(NSDictionary<NSAttributedStringKey,id> *)stringAttributes
+{
+    self = [self init];
+    if (self != nil) {
+        [_stringAttributes addEntriesFromDictionary:stringAttributes];
+    }
+    return self;
+}
+
+- (instancetype) initWithParagraphStyleAttributes:(NSDictionary<CMParagraphStyleAttributeName, id>*)paragraphStyleAttributes
+{
+    self = [self init];
+    if (self != nil) {
+        [_paragraphStyleAttributes addEntriesFromDictionary:paragraphStyleAttributes];
+    }
+    return self;
+}
+
+- (instancetype) initWithStringAttributes:(NSDictionary<NSAttributedStringKey, id>*)stringAttributes
+                 paragraphStyleAttributes:(NSDictionary<CMParagraphStyleAttributeName, id>*)paragraphStyleAttributes
+{
+    self = [self init];
+    if (self != nil) {
+        [_stringAttributes addEntriesFromDictionary:stringAttributes];
+        [_paragraphStyleAttributes addEntriesFromDictionary:paragraphStyleAttributes];
+    }
+    return self;    
+}
+
+- (instancetype) initWithFontSymbolicTraits:(CMFontSymbolicTraits)fontSymbolicTraits
+{
+    self = [self init];
+    if (self != nil) {
+        [self setFontSymbolicTraits:fontSymbolicTraits];
+    }
+    return self;
+}
+
+- (id)copyWithZone:(nullable NSZone *)zone
+{
+    CMStyleAttributes * copiedAttributes = [CMStyleAttributes new];
+    [copiedAttributes.stringAttributes addEntriesFromDictionary:_stringAttributes];
+    [copiedAttributes.fontAttributes addEntriesFromDictionary:_fontAttributes];
+    [copiedAttributes.paragraphStyleAttributes addEntriesFromDictionary:_paragraphStyleAttributes];
+    return copiedAttributes;
+}
+
+- (void) setFontSymbolicTraits:(CMFontSymbolicTraits)fontSymbolicTraits
+{
+#if TARGET_OS_IPHONE
+    NSDictionary<UIFontDescriptorTraitKey, id> * currentFontTraits = _fontAttributes[UIFontDescriptorTraitsAttribute];
+    if (currentFontTraits == nil) {
+        _fontAttributes[UIFontDescriptorTraitsAttribute] = @{ UIFontSymbolicTrait: @(fontSymbolicTraits) };
+    }
+    else {
+        NSMutableDictionary* newFontTraits = currentFontTraits.mutableCopy;
+        newFontTraits[UIFontSymbolicTrait] = @(fontSymbolicTraits);
+        _fontAttributes[UIFontDescriptorTraitsAttribute] = newFontTraits;
+    }
+#else
+    NSDictionary<NSFontDescriptorTraitKey, id> * currentFontTraits = _fontAttributes[NSFontTraitsAttribute];
+    if (currentFontTraits == nil) {
+        _fontAttributes[NSFontTraitsAttribute] = @{ NSFontSymbolicTrait: @(fontSymbolicTraits) };
+    }
+    else {
+        NSMutableDictionary* newFontTraits = currentFontTraits.mutableCopy;
+        newFontTraits[NSFontSymbolicTrait] = @(fontSymbolicTraits);
+        _fontAttributes[NSFontTraitsAttribute] = newFontTraits;
+    }
+#endif
+}
+
+@end
+
+CMParagraphStyleAttributeName const CMParagraphStyleAttributeLineSpacing = @"lineSpacing";
+CMParagraphStyleAttributeName const CMParagraphStyleAttributeParagraphSpacing = @"paragraphSpacing";
+CMParagraphStyleAttributeName const CMParagraphStyleAttributeAlignment = @"alignment";
+CMParagraphStyleAttributeName const CMParagraphStyleAttributeFirstLineHeadExtraIndent = @"firstLineHeadIndent";
+CMParagraphStyleAttributeName const CMParagraphStyleAttributeHeadExtraIndent = @"headIndent";
+CMParagraphStyleAttributeName const CMParagraphStyleAttributeTailExtraIndent = @"tailIndent";
+CMParagraphStyleAttributeName const CMParagraphStyleAttributeLineBreakMode = @"lineBreakMode";
+CMParagraphStyleAttributeName const CMParagraphStyleAttributeMinimumLineHeight = @"minimumLineHeight";
+CMParagraphStyleAttributeName const CMParagraphStyleAttributeMaximumLineHeight = @"maximumLineHeight";
+CMParagraphStyleAttributeName const CMParagraphStyleAttributeLineHeightMultiple = @"lineHeightMultiple";
+CMParagraphStyleAttributeName const CMParagraphStyleAttributeParagraphSpacingBefore = @"paragraphSpacingBefore";
+CMParagraphStyleAttributeName const CMParagraphStyleAttributeHyphenationFactor = @"hyphenationFactor";
+CMParagraphStyleAttributeName const CMParagraphStyleAttributeListItemLabelIndent = @"listItemLabelIndent";
+CMParagraphStyleAttributeName const CMParagraphStyleAttributeListItemBulletString = @"listItemBulletString";
+CMParagraphStyleAttributeName const CMParagraphStyleAttributeListItemNumberFormat = @"listItemNumberFormat";

+ 29 - 0
CocoaMarkdown/CocoaMarkdown.h

@@ -0,0 +1,29 @@
+//
+//  CocoaMarkdown.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/12/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+//! Project version number for CocoaMarkdown.
+FOUNDATION_EXPORT double CocoaMarkdownVersionNumber;
+
+//! Project version string for CocoaMarkdown.
+FOUNDATION_EXPORT const unsigned char CocoaMarkdownVersionString[];
+
+#import <CocoaMarkdown/CMAttributedStringRenderer.h>
+#import <CocoaMarkdown/CMDocument.h>
+#import <CocoaMarkdown/CMDocument+AttributedStringAdditions.h>
+#import <CocoaMarkdown/CMDocument+HTMLAdditions.h>
+#import <CocoaMarkdown/CMHTMLRenderer.h>
+#import <CocoaMarkdown/CMHTMLStrikethroughTransformer.h>
+#import <CocoaMarkdown/CMHTMLUnderlineTransformer.h>
+#import <CocoaMarkdown/CMHTMLSuperscriptTransformer.h>
+#import <CocoaMarkdown/CMHTMLSubscriptTransformer.h>
+#import <CocoaMarkdown/CMIterator.h>
+#import <CocoaMarkdown/CMNode.h>
+#import <CocoaMarkdown/CMParser.h>
+#import <CocoaMarkdown/CMTextAttributes.h>

+ 41 - 0
CocoaMarkdown/Configuration/cmark_export.h

@@ -0,0 +1,41 @@
+
+#ifndef CMARK_EXPORT_H
+#define CMARK_EXPORT_H
+
+#ifdef CMARK_STATIC_DEFINE
+#  define CMARK_EXPORT
+#  define CMARK_NO_EXPORT
+#else
+#  ifndef CMARK_EXPORT
+#    ifdef libcmark_EXPORTS
+        /* We are building this library */
+#      define CMARK_EXPORT __attribute__((visibility("default")))
+#    else
+        /* We are using this library */
+#      define CMARK_EXPORT __attribute__((visibility("default")))
+#    endif
+#  endif
+
+#  ifndef CMARK_NO_EXPORT
+#    define CMARK_NO_EXPORT __attribute__((visibility("hidden")))
+#  endif
+#endif
+
+#ifndef CMARK_DEPRECATED
+#  define CMARK_DEPRECATED __attribute__ ((__deprecated__))
+#endif
+
+#ifndef CMARK_DEPRECATED_EXPORT
+#  define CMARK_DEPRECATED_EXPORT CMARK_EXPORT CMARK_DEPRECATED
+#endif
+
+#ifndef CMARK_DEPRECATED_NO_EXPORT
+#  define CMARK_DEPRECATED_NO_EXPORT CMARK_NO_EXPORT CMARK_DEPRECATED
+#endif
+
+#define DEFINE_NO_DEPRECATED 0
+#if DEFINE_NO_DEPRECATED
+# define CMARK_NO_DEPRECATED
+#endif
+
+#endif

+ 7 - 0
CocoaMarkdown/Configuration/cmark_version.h

@@ -0,0 +1,7 @@
+#ifndef CMARK_VERSION_H
+#define CMARK_VERSION_H
+
+#define CMARK_VERSION ((0 << 16) | (18 << 8)  | 3)
+#define CMARK_VERSION_STRING "0.18.3"
+
+#endif

+ 23 - 0
CocoaMarkdown/Configuration/config.h

@@ -0,0 +1,23 @@
+#define HAVE_STDBOOL_H
+
+#ifdef HAVE_STDBOOL_H
+  #include <stdbool.h>
+#elif !defined(__cplusplus)
+  typedef char bool;
+#endif
+
+#define HAVE___BUILTIN_EXPECT
+
+#define HAVE___ATTRIBUTE__
+
+#ifdef HAVE___ATTRIBUTE__
+  #define CMARK_ATTRIBUTE(list) __attribute__ (list)
+#else
+  #define CMARK_ATTRIBUTE(list)
+#endif
+
+#define HAVE_VA_COPY
+
+#ifndef HAVE_VA_COPY
+  #define va_copy(dest, src) ((dest) = (src))
+#endif

+ 28 - 0
CocoaMarkdown/Info.plist

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>FMWK</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
+	<key>NSHumanReadableCopyright</key>
+	<string>Copyright © 2015 Indragie Karunaratne. All rights reserved.</string>
+	<key>NSPrincipalClass</key>
+	<string></string>
+</dict>
+</plist>

+ 33 - 0
CocoaMarkdownTests/CMDocumentSpec.m

@@ -0,0 +1,33 @@
+@import Quick;
+@import Nimble;
+
+#import <CocoaMarkdown/CocoaMarkdown.h>
+
+QuickSpecBegin(CMDocumentSpec)
+
+describe(@"initialization", ^{
+    
+    __block NSString *path = nil;
+    
+    beforeSuite(^{
+        path = [[NSBundle bundleForClass:self.class] pathForResource:@"test" ofType:@"md"];
+    });
+    
+    it(@"should initialize from data", ^{
+        NSData *data = [NSData dataWithContentsOfFile:path];
+        CMDocument *document = [[CMDocument alloc] initWithData:data options:0];
+        expect(document.rootNode).toNot(beNil());
+    });
+    
+    it(@"should initialize from a file", ^{
+        CMDocument *document = [[CMDocument alloc] initWithContentsOfFile:path options:0];
+        expect(document.rootNode).toNot(beNil());
+    });
+    
+    it(@"should not initialize for an invalid file path", ^{
+        CMDocument *document = [[CMDocument alloc] initWithContentsOfFile:@"/nonexistent/path" options:0];
+        expect(document).to(beNil());
+    });
+});
+
+QuickSpecEnd

+ 15 - 0
CocoaMarkdownTests/CMHTMLRendererSpec.m

@@ -0,0 +1,15 @@
+@import Quick;
+@import Nimble;
+
+#import <CocoaMarkdown/CocoaMarkdown.h>
+
+QuickSpecBegin(CMHTMLRendererSpec)
+
+it(@"should convert a document to HTML", ^{
+    NSString *path = [[NSBundle bundleForClass:self.class] pathForResource:@"test" ofType:@"md"];
+    CMDocument *document = [[CMDocument alloc] initWithContentsOfFile:path options:0];
+    CMHTMLRenderer *renderer = [[CMHTMLRenderer alloc] initWithDocument:document];
+    expect([renderer render]).toNot(beNil());
+});
+
+QuickSpecEnd

+ 49 - 0
CocoaMarkdownTests/CMIteratorSpec.m

@@ -0,0 +1,49 @@
+@import Quick;
+@import Nimble;
+
+#import <CocoaMarkdown/CocoaMarkdown.h>
+#import "CMNode_Private.h"
+
+QuickSpecBegin(CMIteratorSpec)
+
+it(@"should initialize", ^{
+    CMNode *node = [[CMNode alloc] initWithNode:cmark_node_new(CMARK_NODE_PARAGRAPH) freeWhenDone:YES];
+    CMIterator *iter = [[CMIterator alloc] initWithRootNode:node];
+    expect(iter).toNot(beNil());
+});
+
+it(@"should traverse a node tree", ^{
+    CMNode *parent = [[CMNode alloc] initWithNode:cmark_node_new(CMARK_NODE_DOCUMENT) freeWhenDone:YES];
+    
+    cmark_node *paragraph1 = cmark_node_new(CMARK_NODE_PARAGRAPH);
+    cmark_node *paragraph2 = cmark_node_new(CMARK_NODE_PARAGRAPH);
+    cmark_node_append_child(parent.node, paragraph1);
+    cmark_node_append_child(parent.node, paragraph2);
+    
+    CMIterator *iter = [[CMIterator alloc] initWithRootNode:parent];
+    
+    __block NSInteger nodeCount = 0;
+    __block NSInteger eventBalance = 0;
+    [iter enumerateUsingBlock:^(CMNode *node, CMEventType event, BOOL *stop) {
+        switch (event) {
+            case CMARK_EVENT_ENTER:
+                eventBalance++;
+                break;
+            case CMARK_EVENT_EXIT:
+                eventBalance--;
+                nodeCount++;
+                break;
+            default: break;
+        }
+        if (nodeCount == 2) *stop = YES;
+    }];
+    
+    expect(@(nodeCount)).to(equal(@2));
+    expect(@(eventBalance)).to(equal(@1));
+    
+    [iter resetToNode:parent withEventType:CMEventTypeEnter];
+    expect(iter.currentNode).to(equal(parent));
+    expect(@(iter.currentEvent)).to(equal(@(CMARK_EVENT_ENTER)));
+});
+
+QuickSpecEnd

+ 85 - 0
CocoaMarkdownTests/CMNodeSpec.m

@@ -0,0 +1,85 @@
+@import Quick;
+@import Nimble;
+
+#import <CocoaMarkdown/CocoaMarkdown.h>
+#import "CMNode_Private.h"
+
+QuickSpecBegin(CMNodeSpec)
+
+describe(@"tree traversal", ^{
+    it(@"should traverse the tree", ^{
+        CMNode *parent = [[CMNode alloc] initWithNode:cmark_node_new(CMARK_NODE_DOCUMENT) freeWhenDone:YES];
+        CMNode *node = [[CMNode alloc] initWithNode:cmark_node_new(CMARK_NODE_PARAGRAPH) freeWhenDone:NO];
+        CMNode *previous = [[CMNode alloc] initWithNode:cmark_node_new(CMARK_NODE_PARAGRAPH) freeWhenDone:NO];
+        CMNode *next = [[CMNode alloc] initWithNode:cmark_node_new(CMARK_NODE_PARAGRAPH) freeWhenDone:NO];
+        
+        cmark_node_append_child(parent.node, node.node);
+        cmark_node_insert_before(node.node, previous.node);
+        cmark_node_insert_after(node.node, next.node);
+        
+        expect(node.parent).to(equal(parent));
+        expect(node.next).to(equal(next));
+        expect(node.previous).to(equal(previous));
+        expect(parent.firstChild).to(equal(previous));
+        expect(parent.lastChild).to(equal(next));
+    });
+});
+
+describe(@"attributes", ^{
+    it(@"should get general attributes", ^{
+        cmark_node *para = cmark_node_new(CMARK_NODE_PARAGRAPH);
+        CMNode *node = [[CMNode alloc] initWithNode:para freeWhenDone:YES];
+        expect(@(node.type)).to(equal(@(CMARK_NODE_PARAGRAPH)));
+        expect(node.humanReadableType).toNot(beNil());
+    });
+    
+    it(@"should get text attributes", ^{
+        cmark_node *text = cmark_node_new(CMARK_NODE_TEXT);
+        cmark_node_set_literal(text, "hello world");
+        
+        CMNode *node = [[CMNode alloc] initWithNode:text freeWhenDone:YES];
+        expect(node.stringValue).to(equal(@"hello world"));
+    });
+    
+    it(@"should get header attributes", ^{
+        cmark_node *header = cmark_node_new(CMARK_NODE_HEADER);
+        cmark_node_set_header_level(header, 2);
+        
+        CMNode *node = [[CMNode alloc] initWithNode:header freeWhenDone:YES];
+        expect(@(node.headerLevel)).to(equal(@2));
+    });
+    
+    it(@"should get fenced code attributes", ^{
+        cmark_node *code = cmark_node_new(CMARK_NODE_CODE_BLOCK);
+        cmark_node_set_fence_info(code, "objective-c");
+        
+        CMNode *node = [[CMNode alloc] initWithNode:code freeWhenDone:YES];
+        expect(node.fencedCodeInfo).to(equal(@"objective-c"));
+    });
+    
+    it(@"should get list attributes", ^{
+        cmark_node *list = cmark_node_new(CMARK_NODE_LIST);
+        cmark_node_set_list_type(list, CMARK_ORDERED_LIST);
+        cmark_node_set_list_delim(list, CMARK_PERIOD_DELIM);
+        cmark_node_set_list_tight(list, 1);
+        cmark_node_set_list_start(list, 2);
+        
+        CMNode *node = [[CMNode alloc] initWithNode:list freeWhenDone:YES];
+        expect(@(node.listType)).to(equal(@(CMARK_ORDERED_LIST)));
+        expect(@(node.listDelimeterType)).to(equal(@(CMARK_PERIOD_DELIM)));
+        expect(@(node.listTight)).to(beTruthy());
+        expect(@(node.listStartingNumber)).to(equal(@2));
+    });
+    
+    it(@"should get URL attributes", ^{
+        cmark_node *link = cmark_node_new(CMARK_NODE_LINK);
+        cmark_node_set_url(link, "http://indragie.com");
+        cmark_node_set_title(link, "indragie");
+        
+        CMNode *node = [[CMNode alloc] initWithNode:link freeWhenDone:YES];
+        expect(node.URL).to(equal([NSURL URLWithString:@"http://indragie.com"]));
+        expect(node.title).to(equal(@"indragie"));
+    });
+});
+
+QuickSpecEnd

+ 85 - 0
CocoaMarkdownTests/CMParserSpec.m

@@ -0,0 +1,85 @@
+@import Quick;
+@import Nimble;
+
+#import <CocoaMarkdown/CocoaMarkdown.h>
+#import "CMParserTestObject.h"
+
+QuickSpecBegin(CMParserSpec)
+
+__block CMParserTestObject *results = nil;
+__block CMDocument *document = nil;
+
+beforeSuite(^{
+    NSString *path = [[NSBundle bundleForClass:self.class] pathForResource:@"test" ofType:@"md"];
+    document = [[CMDocument alloc] initWithContentsOfFile:path options:0];
+});
+
+beforeEach(^{
+    results = [[CMParserTestObject alloc] initWithDocument:document];
+});
+
+it(@"should parse a document", ^{
+    [results parse];
+
+    expect(@(results.didEndDocument)).to(equal(@1));
+    expect(@(results.didStartDocument)).to(equal(@1));
+    expect(@(results.didAbort)).to(equal(@0));
+    
+    expect(@(results.foundText.count)).to(equal(@27));
+    expect(@(results.foundHRule)).to(equal(@1));
+    
+    expect(results.didStartHeader).to(equal(@[@1, @3]));
+    expect(results.didEndHeader).to(equal(@[@1, @3]));
+    
+    expect(@(results.didStartParagraph)).to(equal(@15));
+    expect(@(results.didEndParagraph)).to(equal(@15));
+    
+    expect(@(results.didStartEmphasis)).to(equal(@2));
+    expect(@(results.didEndEmphasis)).to(equal(@2));
+    
+    expect(@(results.didStartStrong)).to(equal(@2));
+    expect(@(results.didEndStrong)).to(equal(@2));
+    
+    NSArray *links = @[@[[NSURL URLWithString:@"http://indragie.com"], @"Indragie"]];
+    expect(results.didStartLink).to(equal(links));
+    expect(results.didEndLink).to(equal(links));
+    
+    NSArray *images = @[@[[NSURL URLWithString:@"https://raw.githubusercontent.com/sonoramac/Sonora/master/screenshot.png"], @"screenshot"]];
+    expect(results.didStartImage).to(equal(images));
+    expect(results.didEndImage).to(equal(images));
+    
+    expect(@(results.foundHTML.count)).to(equal(@1));
+    expect(@([results.foundHTML[0] hasPrefix:@"<table>"])).to(beTruthy());
+    expect(@([results.foundHTML[0] hasSuffix:@"</table>\n"])).to(beTruthy());
+    expect(results.foundInlineHTML).to(equal(@[@"<s>", @"</s>", @"<sup>", @"</sup>", @"<u>", @"</u>"]));
+    
+    expect(@(results.foundCodeBlock.count)).to(equal(@1));
+    expect(results.foundCodeBlock[0][0]).toNot(beNil());
+    expect(results.foundCodeBlock[0][1]).to(equal(@"c"));
+    expect(results.foundInlineCode).to(equal(@[@"inline code"]));
+    
+    expect(@(results.foundSoftBreak)).to(equal(@1));
+    expect(@(results.foundLineBreak)).to(equal(@1));
+    
+    expect(@(results.didStartBlockQuote)).to(equal(@1));
+    expect(@(results.didEndBlockQuote)).to(equal(@1));
+    
+    expect(results.didStartUnorderedList).to(equal(@[@YES, @YES, @YES]));
+    expect(results.didEndUnorderedList).to(equal(@[@YES, @YES, @YES]));
+    
+    expect(results.didStartOrderedList).to(equal(@[@[@2, @YES]]));
+    expect(results.didEndOrderedList).to(equal(@[@[@2, @YES]]));
+    
+    expect(@(results.didStartListItem)).to(equal(@9));
+    expect(@(results.didEndListItem)).to(equal(@9));
+});
+
+it(@"should abort parsing", ^{
+    results.abortOnStart = YES;
+    [results parse];
+    
+    expect(@(results.didAbort)).to(equal(@1));
+    expect(@(results.didEndDocument)).to(equal(@0));
+});
+
+QuickSpecEnd

+ 50 - 0
CocoaMarkdownTests/CMParserTestObject.h

@@ -0,0 +1,50 @@
+//
+//  CMParserTestObject.h
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/14/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class CMDocument;
+
+@interface CMParserTestObject : NSObject
+@property (nonatomic, readonly) NSInteger didStartDocument;
+@property (nonatomic, readonly) NSInteger didEndDocument;
+@property (nonatomic, readonly) NSInteger didAbort;
+@property (nonatomic, readonly) NSArray/*<NSString>*/ *foundText;
+@property (nonatomic, readonly) NSInteger foundHRule;
+@property (nonatomic, readonly) NSArray/*<NSNumber>*/ *didStartHeader;
+@property (nonatomic, readonly) NSArray/*<NSNumber>*/ *didEndHeader;
+@property (nonatomic, readonly) NSInteger didStartParagraph;
+@property (nonatomic, readonly) NSInteger didEndParagraph;
+@property (nonatomic, readonly) NSInteger didStartEmphasis;
+@property (nonatomic, readonly) NSInteger didEndEmphasis;
+@property (nonatomic, readonly) NSInteger didStartStrong;
+@property (nonatomic, readonly) NSInteger didEndStrong;
+@property (nonatomic, readonly) NSArray/*<NSArray(NSURL, NSString)>*/ *didStartLink;
+@property (nonatomic, readonly) NSArray/*<NSArray(NSURL, NSString)>*/ *didEndLink;
+@property (nonatomic, readonly) NSArray/*<NSArray(NSURL, NSString)>*/ *didStartImage;
+@property (nonatomic, readonly) NSArray/*<NSArray(NSURL, NSString)>*/ *didEndImage;
+@property (nonatomic, readonly) NSArray/*<NSString>*/ *foundHTML;
+@property (nonatomic, readonly) NSArray/*<NSString>*/ *foundInlineHTML;
+@property (nonatomic, readonly) NSArray/*<NSArray(NSString, NSString)>*/ *foundCodeBlock;
+@property (nonatomic, readonly) NSArray/*<NSString>*/ *foundInlineCode;
+@property (nonatomic, readonly) NSInteger foundSoftBreak;
+@property (nonatomic, readonly) NSInteger foundLineBreak;
+@property (nonatomic, readonly) NSInteger didStartBlockQuote;
+@property (nonatomic, readonly) NSInteger didEndBlockQuote;
+@property (nonatomic, readonly) NSArray/*<NSNumber>*/ *didStartUnorderedList;
+@property (nonatomic, readonly) NSArray/*<NSNumber>*/ *didEndUnorderedList;
+@property (nonatomic, readonly) NSArray/*<NSArray(NSNumber, NSNumber)>*/ *didStartOrderedList;
+@property (nonatomic, readonly) NSArray/*<NSArray(NSNumber, NSNumber)>*/ *didEndOrderedList;
+@property (nonatomic, readonly) NSInteger didStartListItem;
+@property (nonatomic, readonly) NSInteger didEndListItem;
+
+@property (nonatomic) BOOL abortOnStart;
+
+- (instancetype)initWithDocument:(CMDocument *)document;
+- (void)parse;
+@end

+ 231 - 0
CocoaMarkdownTests/CMParserTestObject.m

@@ -0,0 +1,231 @@
+//
+//  CMParserTestObject.m
+//  CocoaMarkdown
+//
+//  Created by Indragie on 1/14/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+#import "CMParserTestObject.h"
+#import <CocoaMarkdown/CocoaMarkdown.h>
+
+@interface CMParserTestObject () <CMParserDelegate>
+@end
+
+@implementation CMParserTestObject {
+    CMParser *_parser;
+    NSMutableArray *_foundText;
+    NSMutableArray *_didStartHeader;
+    NSMutableArray *_didEndHeader;
+    NSMutableArray *_didStartLink;
+    NSMutableArray *_didEndLink;
+    NSMutableArray *_didStartImage;
+    NSMutableArray *_didEndImage;
+    NSMutableArray *_foundHTML;
+    NSMutableArray *_foundInlineHTML;
+    NSMutableArray *_foundCodeBlock;
+    NSMutableArray *_foundInlineCode;
+    NSMutableArray *_didStartUnorderedList;
+    NSMutableArray *_didEndUnorderedList;
+    NSMutableArray *_didStartOrderedList;
+    NSMutableArray *_didEndOrderedList;
+}
+
+- (instancetype)initWithDocument:(CMDocument *)document
+{
+    NSParameterAssert(document);
+    if ((self = [super init])) {
+        _parser = [[CMParser alloc] initWithDocument:document delegate:self];
+        
+        _foundText = [[NSMutableArray alloc] init];
+        _didStartHeader = [[NSMutableArray alloc] init];
+        _didEndHeader = [[NSMutableArray alloc] init];
+        _didStartLink = [[NSMutableArray alloc] init];
+        _didEndLink = [[NSMutableArray alloc] init];
+        _didStartImage = [[NSMutableArray alloc] init];
+        _didEndImage = [[NSMutableArray alloc] init];
+        _foundHTML = [[NSMutableArray alloc] init];
+        _foundInlineHTML = [[NSMutableArray alloc] init];
+        _foundCodeBlock = [[NSMutableArray alloc] init];
+        _foundInlineCode = [[NSMutableArray alloc] init];
+        _didStartUnorderedList = [[NSMutableArray alloc] init];
+        _didEndUnorderedList = [[NSMutableArray alloc] init];
+        _didStartOrderedList = [[NSMutableArray alloc] init];
+        _didEndOrderedList = [[NSMutableArray alloc] init];
+    }
+    return self;
+}
+
+- (void)parse
+{
+    [_parser parse];
+}
+
+#pragma mark - CMParserDelegate
+
+- (void)parserDidStartDocument:(CMParser *)parser
+{
+    _didStartDocument++;
+    if (_abortOnStart) {
+        [parser abortParsing];
+    }
+}
+
+- (void)parserDidEndDocument:(CMParser *)parser
+{
+    _didEndDocument++;
+}
+
+- (void)parserDidAbort:(CMParser *)parser
+{
+    _didAbort++;
+}
+
+- (void)parser:(CMParser *)parser foundText:(NSString *)text
+{
+    [_foundText addObject:text];
+}
+
+- (void)parserFoundHRule:(CMParser *)parser
+{
+    _foundHRule++;
+}
+
+- (void)parser:(CMParser *)parser didStartHeaderWithLevel:(NSInteger)level
+{
+    [_didStartHeader addObject:@(level)];
+}
+
+- (void)parser:(CMParser *)parser didEndHeaderWithLevel:(NSInteger)level
+{
+    [_didEndHeader addObject:@(level)];
+}
+
+- (void)parserDidStartParagraph:(CMParser *)parser
+{
+    _didStartParagraph++;
+}
+
+- (void)parserDidEndParagraph:(CMParser *)parser
+{
+    _didEndParagraph++;
+}
+
+- (void)parserDidStartEmphasis:(CMParser *)parser
+{
+    _didStartEmphasis++;
+}
+
+- (void)parserDidEndEmphasis:(CMParser *)parser
+{
+    _didEndEmphasis++;
+}
+
+- (void)parserDidStartStrong:(CMParser *)parser
+{
+    _didStartStrong++;
+}
+
+- (void)parserDidEndStrong:(CMParser *)parser
+{
+    _didEndStrong++;
+}
+
+- (void)parser:(CMParser *)parser didStartLinkWithURL:(NSURL *)URL title:(NSString *)title
+{
+    NSParameterAssert(URL);
+    [_didStartLink addObject:@[URL, title ?: NSNull.null]];
+}
+
+- (void)parser:(CMParser *)parser didEndLinkWithURL:(NSURL *)URL title:(NSString *)title
+{
+    NSParameterAssert(URL);
+    [_didEndLink addObject:@[URL, title ?: NSNull.null]];
+}
+
+- (void)parser:(CMParser *)parser didStartImageWithURL:(NSURL *)URL title:(NSString *)title
+{
+    NSParameterAssert(URL);
+    [_didStartImage addObject:@[URL, title ?: NSNull.null]];
+}
+
+- (void)parser:(CMParser *)parser didEndImageWithURL:(NSURL *)URL title:(NSString *)title
+{
+    NSParameterAssert(URL);
+    [_didEndImage addObject:@[URL, title ?: NSNull.null]];
+}
+
+- (void)parser:(CMParser *)parser foundHTML:(NSString *)HTML
+{
+    NSParameterAssert(HTML);
+    [_foundHTML addObject:HTML];
+}
+
+- (void)parser:(CMParser *)parser foundInlineHTML:(NSString *)HTML
+{
+    NSParameterAssert(HTML);
+    [_foundInlineHTML addObject:HTML];
+}
+
+- (void)parser:(CMParser *)parser foundCodeBlock:(NSString *)code info:(NSString *)info
+{
+    NSParameterAssert(code);
+    [_foundCodeBlock addObject:@[code, info ?: NSNull.null]];
+}
+
+- (void)parser:(CMParser *)parser foundInlineCode:(NSString *)code
+{
+    [_foundInlineCode addObject:code];
+}
+
+- (void)parserFoundSoftBreak:(CMParser *)parser
+{
+    _foundSoftBreak++;
+}
+
+- (void)parserFoundLineBreak:(CMParser *)parser
+{
+    _foundLineBreak++;
+}
+
+- (void)parserDidStartBlockQuote:(CMParser *)parser
+{
+    _didStartBlockQuote++;
+}
+
+- (void)parserDidEndBlockQuote:(CMParser *)parser
+{
+    _didEndBlockQuote++;
+}
+
+- (void)parser:(CMParser *)parser didStartUnorderedListWithTightness:(BOOL)tight
+{
+    [_didStartUnorderedList addObject:@(tight)];
+}
+
+- (void)parser:(CMParser *)parser didEndUnorderedListWithTightness:(BOOL)tight
+{
+    [_didEndUnorderedList addObject:@(tight)];
+}
+
+- (void)parser:(CMParser *)parser didStartOrderedListWithStartingNumber:(NSInteger)num tight:(BOOL)tight
+{
+    [_didStartOrderedList addObject:@[@(num), @(tight)]];
+}
+
+- (void)parser:(CMParser *)parser didEndOrderedListWithStartingNumber:(NSInteger)num tight:(BOOL)tight
+{
+    [_didEndOrderedList addObject:@[@(num), @(tight)]];
+}
+
+- (void)parserDidStartListItem:(CMParser *)parser
+{
+    _didStartListItem++;
+}
+
+- (void)parserDidEndListItem:(CMParser *)parser
+{
+    _didEndListItem++;
+}
+
+@end

+ 4 - 0
CocoaMarkdownTests/CocoaMarkdownTests-Bridging-Header.h

@@ -0,0 +1,4 @@
+//
+//  Use this file to import your target's public headers that you would like to expose to Swift.
+//
+

+ 10 - 0
CocoaMarkdownTests/DummySpec.swift

@@ -0,0 +1,10 @@
+import Quick
+import Nimble
+
+// Dummy spec just to force Xcode to copy the Swift runtime into the test bundle
+// Otherwise the tests fail immediately.
+class DummySpec: QuickSpec {
+    override func spec() {
+
+    }
+}

+ 24 - 0
CocoaMarkdownTests/Info.plist

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>

+ 45 - 0
CocoaMarkdownTests/Resources/test.md

@@ -0,0 +1,45 @@
+# Test Document
+### Testing some basic styles
+
+Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo  
+sit amet risus. Praesent co mmodo cursus magna, vel scelerisque nisl consectetur et.
+
+Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Nullam id dolor id nibh ultricies vehicula ut id elit. Sed posuere consectetur est at lobortis.
+
+Duis mollis, <s>est non commodo</s> luctus, nisi erat porttitor ligula, eget lacinia odio <sup>sem</sup> nec elit. <u>Morbi leo risus</u>, porta ac consectetur ac, vestibulum at eros.
+[Testing link](http://indragie.com "Indragie") and `inline code`.
+
+2. **Bold text**
+3. _Italic text_
+4. **_Bold italic text_**
+
+Here is another list:
+
+* Unordered
+    - sublist
+* list
+    - With
+    - two
+    - sublists
+
+![Image](https://raw.githubusercontent.com/sonoramac/Sonora/master/screenshot.png "screenshot")
+
+---
+
+> This is a block quote
+
+```c
+int main(int argc, char *argv[]) {
+	puts("Hello world");
+	return EXIT_SUCCESS;
+}
+```
+
+<table>
+  <tr>
+    <td>Table</td>
+  </tr>
+  <tr>
+    <td>Row</td>
+  </tr>
+</table>

+ 89 - 0
Example-Mac/AppDelegate.swift

@@ -0,0 +1,89 @@
+//
+//  AppDelegate.swift
+//  Example-Mac
+//
+//  Created by Indragie on 1/15/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+import Cocoa
+import CocoaMarkdown
+
+@NSApplicationMain
+class AppDelegate: NSObject, NSApplicationDelegate {
+
+    @IBOutlet weak var window: NSWindow!
+    @IBOutlet var textView: NSTextView!
+    @IBOutlet weak var openPanelAUxiliaryView: NSView!
+    @IBOutlet weak var linksBaseUrlTextField: NSTextField!
+    
+    func applicationDidFinishLaunching(_ notification: Notification) {
+        if let testFileUrl = Bundle.main.url(forResource:"test", withExtension: "md") {
+            renderMarkdownFile(atUrl: testFileUrl)
+        }
+    }
+    
+    @IBAction func selectMarkdownFile(_ sender: Any) {
+        let panel = NSOpenPanel()
+        panel.allowedFileTypes = ["md", "markdown"]
+        panel.allowsMultipleSelection = false
+        panel.prompt = "Render"
+        panel.accessoryView = openPanelAUxiliaryView
+        if #available(OSX 10.11, *) {
+            panel.isAccessoryViewDisclosed = true
+        }
+        
+        panel.beginSheetModal(for: window, completionHandler: { (response) in
+            if response == .OK {
+                if let selectedFileUrl = panel.url {
+                    
+                    var linksBaseUrl: URL? = nil
+                    if self.linksBaseUrlTextField.stringValue.count > 0 {
+                        linksBaseUrl = URL(string: self.linksBaseUrlTextField.stringValue)
+                    }
+                    
+                    self.renderMarkdownFile(atUrl: selectedFileUrl, withLinksBaseUrl: linksBaseUrl)
+                }
+            }
+        })
+    }
+    
+    
+    func renderMarkdownFile(atUrl url: URL, withLinksBaseUrl linksBaseUrl: URL? = nil) {
+        let document = CMDocument(contentsOfFile: url.path, options: CMDocumentOptions(rawValue: 0))
+        if linksBaseUrl != nil {
+            document?.linksBaseURL = linksBaseUrl
+        }
+        
+        let textAttributes = CMTextAttributes()
+        
+        // Customize the color and font of header elements
+        textAttributes.addStringAttributes([ .foregroundColor: NSColor(red: 0.0, green: 0.446, blue: 0.657, alpha: 1.0)], forElementWithKinds: .anyHeader)
+        if #available(OSX 10.11, *) {
+            textAttributes.addFontAttributes([ .family: "Avenir Next" ,
+                                                .traits: [ NSFontDescriptor.TraitKey.symbolic: NSFontDescriptor.SymbolicTraits.italic.rawValue,
+                                                           NSFontDescriptor.TraitKey.weight: NSFont.Weight.semibold]], 
+                                              forElementWithKinds: .anyHeader)
+            // Set specific font traits for header1 and header2
+            textAttributes.setFontTraits([.weight: NSFont.Weight.heavy], forElementWithKinds: [.header1, .header2])
+        } 
+        textAttributes.addFontAttributes([ .size: 48 ], forElementWithKinds: .header1)
+        
+        // Customize the font and paragraph alignment of block-quotes
+        textAttributes.addFontAttributes([.family: "Snell Roundhand", .size: 19], forElementWithKinds: .blockQuote)
+        textAttributes.addParagraphStyleAttributes([ .alignment: NSTextAlignment.center.rawValue], forElementWithKinds: .blockQuote)
+        
+        // Customize the background color of code elements
+        textAttributes.addStringAttributes([ .backgroundColor: NSColor(white: 0.9, alpha: 0.5)], forElementWithKinds: [.inlineCode, .codeBlock])
+        
+        let renderer = CMAttributedStringRenderer(document: document, attributes: textAttributes)!
+        renderer.register(CMHTMLStrikethroughTransformer())
+        renderer.register(CMHTMLSuperscriptTransformer())
+        renderer.register(CMHTMLUnderlineTransformer())
+        
+        if let renderedAttributedString = renderer.render(),
+            let textStorage = textView.textStorage {
+            textStorage.replaceCharacters(in: NSRange(location: 0, length: textStorage.length), with: renderedAttributedString)
+        }
+    }
+}

+ 779 - 0
Example-Mac/Base.lproj/MainMenu.xib

@@ -0,0 +1,779 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+    <dependencies>
+        <deployment identifier="macosx"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
+            <connections>
+                <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
+            </connections>
+        </customObject>
+        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+        <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+        <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Example_Mac" customModuleProvider="target">
+            <connections>
+                <outlet property="linksBaseUrlTextField" destination="nGp-4p-Ra1" id="Khz-Wt-mnr"/>
+                <outlet property="openPanelAUxiliaryView" destination="Snk-hX-bWZ" id="QoC-fr-56o"/>
+                <outlet property="textView" destination="ZAy-oj-qvP" id="gDG-Nn-WgH"/>
+                <outlet property="window" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
+            </connections>
+        </customObject>
+        <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
+        <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
+            <items>
+                <menuItem title="Example-Mac" id="1Xt-HY-uBw">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="Example-Mac" systemMenu="apple" id="uQy-DD-JDr">
+                        <items>
+                            <menuItem title="About Example-Mac" id="5kV-Vb-QxS">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
+                            <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
+                            <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
+                            <menuItem title="Services" id="NMo-om-nkz">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
+                            <menuItem title="Hide Example-Mac" keyEquivalent="h" id="Olw-nP-bQN">
+                                <connections>
+                                    <action selector="hide:" target="-1" id="PnN-Uc-m68"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                <connections>
+                                    <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Show All" id="Kd2-mp-pUS">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
+                            <menuItem title="Quit Example-Mac" keyEquivalent="q" id="4sb-4s-VLi">
+                                <connections>
+                                    <action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="File" id="dMs-cI-mzQ">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="File" id="bib-Uj-vzu">
+                        <items>
+                            <menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
+                                <connections>
+                                    <action selector="newDocument:" target="-1" id="4Si-XN-c54"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
+                                <connections>
+                                    <action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Open Recent" id="tXI-mr-wws">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
+                                    <items>
+                                        <menuItem title="Clear Menu" id="vNY-rz-j42">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
+                            <menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
+                                <connections>
+                                    <action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
+                                <connections>
+                                    <action selector="saveDocument:" target="-1" id="teZ-XB-qJY"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
+                                <connections>
+                                    <action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Revert to Saved" id="KaW-ft-85H">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="revertDocumentToSaved:" target="-1" id="iJ3-Pv-kwq"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
+                            <menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
+                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+                                <connections>
+                                    <action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
+                                <connections>
+                                    <action selector="print:" target="-1" id="qaZ-4w-aoO"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Edit" id="5QF-Oa-p0T">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="Edit" id="W48-6f-4Dl">
+                        <items>
+                            <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
+                                <connections>
+                                    <action selector="undo:" target="-1" id="M6e-cu-g7V"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
+                                <connections>
+                                    <action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
+                            <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
+                                <connections>
+                                    <action selector="cut:" target="-1" id="YJe-68-I9s"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
+                                <connections>
+                                    <action selector="copy:" target="-1" id="G1f-GL-Joy"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
+                                <connections>
+                                    <action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                <connections>
+                                    <action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Delete" id="pa3-QI-u2k">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
+                                <connections>
+                                    <action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
+                            <menuItem title="Find" id="4EN-yA-p0u">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Find" id="1b7-l0-nxx">
+                                    <items>
+                                        <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
+                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
+                                            <connections>
+                                                <action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
+                                            <connections>
+                                                <action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
+                                    <items>
+                                        <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
+                                            <connections>
+                                                <action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
+                                            <connections>
+                                                <action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
+                                        <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Substitutions" id="9ic-FL-obx">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
+                                    <items>
+                                        <menuItem title="Show Substitutions" id="z6F-FW-3nz">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
+                                        <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smart Quotes" id="hQb-2v-fYv">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smart Dashes" id="rgM-f4-ycn">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smart Links" id="cwL-P1-jid">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Data Detectors" id="tRr-pd-1PS">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Text Replacement" id="HFQ-gK-NFA">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Transformations" id="2oI-Rn-ZJC">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
+                                    <items>
+                                        <menuItem title="Make Upper Case" id="vmV-6d-7jI">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Make Lower Case" id="d9M-CD-aMd">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Capitalize" id="UEZ-Bs-lqG">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Speech" id="xrE-MZ-jX0">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
+                                    <items>
+                                        <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Format" id="jxT-CU-nIS">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="Format" id="GEO-Iw-cKr">
+                        <items>
+                            <menuItem title="Font" id="Gi5-1S-RQB">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
+                                    <items>
+                                        <menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
+                                            <connections>
+                                                <action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
+                                            <connections>
+                                                <action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
+                                            <connections>
+                                                <action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
+                                            <connections>
+                                                <action selector="underline:" target="-1" id="FYS-2b-JAY"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
+                                        <menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
+                                            <connections>
+                                                <action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
+                                            <connections>
+                                                <action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
+                                        <menuItem title="Kern" id="jBQ-r6-VK2">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Kern" id="tlD-Oa-oAM">
+                                                <items>
+                                                    <menuItem title="Use Default" id="GUa-eO-cwY">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="useStandardKerning:" target="-1" id="6dk-9l-Ckg"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Use None" id="cDB-IK-hbR">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="turnOffKerning:" target="-1" id="U8a-gz-Maa"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Tighten" id="46P-cB-AYj">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="tightenKerning:" target="-1" id="hr7-Nz-8ro"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Loosen" id="ogc-rX-tC1">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="loosenKerning:" target="-1" id="8i4-f9-FKE"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                        <menuItem title="Ligatures" id="o6e-r0-MWq">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
+                                                <items>
+                                                    <menuItem title="Use Default" id="agt-UL-0e3">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="useStandardLigatures:" target="-1" id="7uR-wd-Dx6"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Use None" id="J7y-lM-qPV">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="turnOffLigatures:" target="-1" id="iX2-gA-Ilz"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Use All" id="xQD-1f-W4t">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="useAllLigatures:" target="-1" id="KcB-kA-TuK"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                        <menuItem title="Baseline" id="OaQ-X3-Vso">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Baseline" id="ijk-EB-dga">
+                                                <items>
+                                                    <menuItem title="Use Default" id="3Om-Ey-2VK">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="unscript:" target="-1" id="0vZ-95-Ywn"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Superscript" id="Rqc-34-cIF">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="superscript:" target="-1" id="3qV-fo-wpU"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Subscript" id="I0S-gh-46l">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="subscript:" target="-1" id="Q6W-4W-IGz"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Raise" id="2h7-ER-AoG">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="raiseBaseline:" target="-1" id="4sk-31-7Q9"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem title="Lower" id="1tx-W0-xDw">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="lowerBaseline:" target="-1" id="OF1-bc-KW4"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
+                                        <menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
+                                            <connections>
+                                                <action selector="orderFrontColorPanel:" target="-1" id="mSX-Xz-DV3"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
+                                        <menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
+                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="copyFont:" target="-1" id="GJO-xA-L4q"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
+                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="pasteFont:" target="-1" id="JfD-CL-leO"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                            <menuItem title="Text" id="Fal-I4-PZk">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <menu key="submenu" title="Text" id="d9c-me-L2H">
+                                    <items>
+                                        <menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
+                                            <connections>
+                                                <action selector="alignLeft:" target="-1" id="zUv-R1-uAa"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
+                                            <connections>
+                                                <action selector="alignCenter:" target="-1" id="spX-mk-kcS"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Justify" id="J5U-5w-g23">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="alignJustified:" target="-1" id="ljL-7U-jND"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
+                                            <connections>
+                                                <action selector="alignRight:" target="-1" id="r48-bG-YeY"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
+                                        <menuItem title="Writing Direction" id="H1b-Si-o9J">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
+                                                <items>
+                                                    <menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                    </menuItem>
+                                                    <menuItem id="YGs-j5-SAR">
+                                                        <string key="title">	Default</string>
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="makeBaseWritingDirectionNatural:" target="-1" id="qtV-5e-UBP"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem id="Lbh-J2-qVU">
+                                                        <string key="title">	Left to Right</string>
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="S0X-9S-QSf"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem id="jFq-tB-4Kx">
+                                                        <string key="title">	Right to Left</string>
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="5fk-qB-AqJ"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
+                                                    <menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                    </menuItem>
+                                                    <menuItem id="Nop-cj-93Q">
+                                                        <string key="title">	Default</string>
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="makeTextWritingDirectionNatural:" target="-1" id="lPI-Se-ZHp"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem id="BgM-ve-c93">
+                                                        <string key="title">	Left to Right</string>
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="caW-Bv-w94"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                    <menuItem id="RB4-Sm-HuC">
+                                                        <string key="title">	Right to Left</string>
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
+                                                        <connections>
+                                                            <action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="EXD-6r-ZUu"/>
+                                                        </connections>
+                                                    </menuItem>
+                                                </items>
+                                            </menu>
+                                        </menuItem>
+                                        <menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
+                                        <menuItem title="Show Ruler" id="vLm-3I-IUL">
+                                            <modifierMask key="keyEquivalentModifierMask"/>
+                                            <connections>
+                                                <action selector="toggleRuler:" target="-1" id="FOx-HJ-KwY"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
+                                            <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="copyRuler:" target="-1" id="71i-fW-3W2"/>
+                                            </connections>
+                                        </menuItem>
+                                        <menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
+                                            <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+                                            <connections>
+                                                <action selector="pasteRuler:" target="-1" id="cSh-wd-qM2"/>
+                                            </connections>
+                                        </menuItem>
+                                    </items>
+                                </menu>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="View" id="H8h-7b-M4v">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="View" id="HyV-fh-RgO">
+                        <items>
+                            <menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+                                <connections>
+                                    <action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="runToolbarCustomizationPalette:" target="-1" id="pQI-g3-MTW"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Window" id="aUF-d1-5bR">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
+                        <items>
+                            <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
+                                <connections>
+                                    <action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem title="Zoom" id="R4o-n2-Eq4">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
+                                </connections>
+                            </menuItem>
+                            <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
+                            <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
+                                <modifierMask key="keyEquivalentModifierMask"/>
+                                <connections>
+                                    <action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+                <menuItem title="Help" id="wpr-3q-Mcd">
+                    <modifierMask key="keyEquivalentModifierMask"/>
+                    <menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
+                        <items>
+                            <menuItem title="Example-Mac Help" keyEquivalent="?" id="FKE-Sm-Kum">
+                                <connections>
+                                    <action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
+                                </connections>
+                            </menuItem>
+                        </items>
+                    </menu>
+                </menuItem>
+            </items>
+            <point key="canvasLocation" x="153" y="-176"/>
+        </menu>
+        <window title="Example-Mac" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
+            <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
+            <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
+            <rect key="contentRect" x="335" y="390" width="480" height="360"/>
+            <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
+            <view key="contentView" id="EiT-Mj-1SZ">
+                <rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <scrollView horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ox9-K4-OpB">
+                        <rect key="frame" x="20" y="61" width="440" height="279"/>
+                        <clipView key="contentView" drawsBackground="NO" id="cDP-uz-M9g">
+                            <rect key="frame" x="1" y="1" width="438" height="277"/>
+                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                            <subviews>
+                                <textView editable="NO" verticallyResizable="YES" findStyle="panel" usesRuler="YES" allowsNonContiguousLayout="YES" quoteSubstitution="YES" dashSubstitution="YES" smartInsertDelete="YES" id="ZAy-oj-qvP">
+                                    <rect key="frame" x="0.0" y="0.0" width="438" height="277"/>
+                                    <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                                    <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+                                    <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                                    <size key="minSize" width="438" height="277"/>
+                                    <size key="maxSize" width="463" height="10000000"/>
+                                    <color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+                                </textView>
+                            </subviews>
+                        </clipView>
+                        <scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="VAU-VQ-6rw">
+                            <rect key="frame" x="-100" y="-100" width="87" height="18"/>
+                            <autoresizingMask key="autoresizingMask"/>
+                        </scroller>
+                        <scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="Qyp-8K-sJQ">
+                            <rect key="frame" x="423" y="1" width="16" height="277"/>
+                            <autoresizingMask key="autoresizingMask"/>
+                        </scroller>
+                    </scrollView>
+                    <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="vY6-Hh-wf5">
+                        <rect key="frame" x="150" y="13" width="180" height="32"/>
+                        <buttonCell key="cell" type="push" title="Select Markdown File…" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="azU-W1-ykT">
+                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                            <font key="font" metaFont="system"/>
+                        </buttonCell>
+                        <connections>
+                            <action selector="selectMarkdownFile:" target="Voe-Tx-rLC" id="isY-7n-bsd"/>
+                        </connections>
+                    </button>
+                </subviews>
+                <constraints>
+                    <constraint firstItem="vY6-Hh-wf5" firstAttribute="centerX" secondItem="EiT-Mj-1SZ" secondAttribute="centerX" id="7D8-uu-bYM"/>
+                    <constraint firstAttribute="bottom" secondItem="vY6-Hh-wf5" secondAttribute="bottom" constant="20" id="CO7-XM-Ek5"/>
+                    <constraint firstItem="ox9-K4-OpB" firstAttribute="leading" secondItem="EiT-Mj-1SZ" secondAttribute="leading" constant="20" id="K9T-Ng-MIa"/>
+                    <constraint firstItem="ox9-K4-OpB" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" constant="20" id="Qx9-Xz-Bcd"/>
+                    <constraint firstAttribute="trailing" secondItem="ox9-K4-OpB" secondAttribute="trailing" constant="20" id="ROG-VW-Vrf"/>
+                    <constraint firstItem="vY6-Hh-wf5" firstAttribute="top" secondItem="ox9-K4-OpB" secondAttribute="bottom" constant="20" id="vjd-ge-9RY"/>
+                </constraints>
+            </view>
+            <point key="canvasLocation" x="168" y="78"/>
+        </window>
+        <customView id="Snk-hX-bWZ" userLabel="Open Pane Auxiliary View">
+            <rect key="frame" x="0.0" y="0.0" width="500" height="62"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+            <subviews>
+                <customView translatesAutoresizingMaskIntoConstraints="NO" id="pgK-yF-KoU" userLabel="Centering View">
+                    <rect key="frame" x="58" y="0.0" width="385" height="62"/>
+                    <subviews>
+                        <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9Sl-Sz-s6f">
+                            <rect key="frame" x="6" y="23" width="101" height="17"/>
+                            <textFieldCell key="cell" lineBreakMode="clipping" title="Links base URL:" id="PF4-6v-QLi">
+                                <font key="font" usesAppearanceFont="YES"/>
+                                <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                                <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                            </textFieldCell>
+                        </textField>
+                        <textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nGp-4p-Ra1">
+                            <rect key="frame" x="127" y="20" width="250" height="22"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="250" id="HaQ-Cn-cEn"/>
+                            </constraints>
+                            <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" id="QZq-qL-54q">
+                                <font key="font" metaFont="system"/>
+                                <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+                                <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                            </textFieldCell>
+                        </textField>
+                    </subviews>
+                    <constraints>
+                        <constraint firstAttribute="bottom" secondItem="nGp-4p-Ra1" secondAttribute="bottom" constant="20" symbolic="YES" id="Alf-9Y-6gO"/>
+                        <constraint firstItem="9Sl-Sz-s6f" firstAttribute="leading" secondItem="pgK-yF-KoU" secondAttribute="leading" constant="8" id="BkN-Jr-zhk"/>
+                        <constraint firstAttribute="trailing" secondItem="nGp-4p-Ra1" secondAttribute="trailing" constant="8" id="SOS-h3-Smk"/>
+                        <constraint firstItem="9Sl-Sz-s6f" firstAttribute="firstBaseline" secondItem="nGp-4p-Ra1" secondAttribute="firstBaseline" id="im0-qC-lDd"/>
+                        <constraint firstItem="nGp-4p-Ra1" firstAttribute="top" secondItem="pgK-yF-KoU" secondAttribute="top" constant="20" symbolic="YES" id="l3D-ko-qCy"/>
+                        <constraint firstItem="nGp-4p-Ra1" firstAttribute="leading" secondItem="9Sl-Sz-s6f" secondAttribute="trailing" constant="22" id="vsn-hf-qu3"/>
+                    </constraints>
+                </customView>
+            </subviews>
+            <constraints>
+                <constraint firstItem="pgK-yF-KoU" firstAttribute="top" secondItem="Snk-hX-bWZ" secondAttribute="top" id="MpH-Z0-YhU"/>
+                <constraint firstAttribute="bottom" secondItem="pgK-yF-KoU" secondAttribute="bottom" id="WSw-YB-gsR"/>
+                <constraint firstItem="pgK-yF-KoU" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Snk-hX-bWZ" secondAttribute="leading" id="bNd-80-rNT"/>
+                <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="pgK-yF-KoU" secondAttribute="trailing" id="eUz-7i-Phz"/>
+                <constraint firstItem="pgK-yF-KoU" firstAttribute="centerX" secondItem="Snk-hX-bWZ" secondAttribute="centerX" id="ySV-Sd-Wcz"/>
+            </constraints>
+            <point key="canvasLocation" x="181" y="390"/>
+        </customView>
+    </objects>
+</document>

+ 58 - 0
Example-Mac/Images.xcassets/AppIcon.appiconset/Contents.json

@@ -0,0 +1,58 @@
+{
+  "images" : [
+    {
+      "idiom" : "mac",
+      "size" : "16x16",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "16x16",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "32x32",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "32x32",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "128x128",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "128x128",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "256x256",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "256x256",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "512x512",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "mac",
+      "size" : "512x512",
+      "scale" : "2x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

+ 6 - 0
Example-Mac/Images.xcassets/Contents.json

@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

+ 34 - 0
Example-Mac/Info.plist

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIconFile</key>
+	<string></string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSMinimumSystemVersion</key>
+	<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
+	<key>NSHumanReadableCopyright</key>
+	<string>Copyright © 2015 Indragie Karunaratne. All rights reserved.</string>
+	<key>NSMainNibFile</key>
+	<string>MainMenu</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+</dict>
+</plist>

+ 47 - 0
Example-iOS/AppDelegate.swift

@@ -0,0 +1,47 @@
+//
+//  AppDelegate.swift
+//  Example-iOS
+//
+//  Created by Indragie on 1/15/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+import UIKit
+
+@UIApplicationMain
+class AppDelegate: UIResponder, UIApplicationDelegate {
+
+    var window: UIWindow?
+
+
+    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool
+    {
+        // Override point for customization after application launch.
+        return true
+    }
+
+    func applicationWillResignActive(_ application: UIApplication) {
+        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
+        // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
+    }
+
+    func applicationDidEnterBackground(_ application: UIApplication) {
+        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
+        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
+    }
+
+    func applicationWillEnterForeground(_ application: UIApplication) {
+        // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
+    }
+
+    func applicationDidBecomeActive(_ application: UIApplication) {
+        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
+    }
+
+    func applicationWillTerminate(_ application: UIApplication) {
+        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
+    }
+
+
+}
+

+ 41 - 0
Example-iOS/Base.lproj/LaunchScreen.xib

@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6214" systemVersion="14A314h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6207"/>
+        <capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB">
+            <rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="  Copyright (c) 2015 Indragie Karunaratne. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
+                    <rect key="frame" x="20" y="439" width="441" height="21"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                    <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Example-iOS" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
+                    <rect key="frame" x="20" y="140" width="441" height="43"/>
+                    <fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
+                    <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                    <nil key="highlightedColor"/>
+                </label>
+            </subviews>
+            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+            <constraints>
+                <constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
+                <constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
+                <constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
+                <constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
+                <constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
+                <constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
+            </constraints>
+            <nil key="simulatedStatusBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <point key="canvasLocation" x="548" y="455"/>
+        </view>
+    </objects>
+</document>

+ 47 - 0
Example-iOS/Base.lproj/Main.storyboard

@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15400" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="vXZ-lx-hvc">
+    <device id="retina4_7" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15404"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="ufC-wZ-h7g">
+            <objects>
+                <viewController id="vXZ-lx-hvc" customClass="ViewController" customModule="Example_iOS" customModuleProvider="target" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="jyV-Pf-zRb"/>
+                        <viewControllerLayoutGuide type="bottom" id="2fi-mo-0CV"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="kh9-bI-dsS">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                        <subviews>
+                            <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" editable="NO" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="97c-zT-yS4">
+                                <rect key="frame" x="16" y="0.0" width="343" height="647"/>
+                                <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
+                                <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                                <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
+                            </textView>
+                        </subviews>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstItem="2fi-mo-0CV" firstAttribute="top" secondItem="97c-zT-yS4" secondAttribute="bottom" constant="20" id="Im8-I4-8vo"/>
+                            <constraint firstItem="97c-zT-yS4" firstAttribute="trailing" secondItem="kh9-bI-dsS" secondAttribute="trailingMargin" id="Vcm-7c-ljy"/>
+                            <constraint firstItem="97c-zT-yS4" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leadingMargin" id="Y1L-av-rha"/>
+                            <constraint firstItem="97c-zT-yS4" firstAttribute="top" secondItem="jyV-Pf-zRb" secondAttribute="bottom" id="xzb-4G-U6h"/>
+                        </constraints>
+                    </view>
+                    <connections>
+                        <outlet property="textView" destination="97c-zT-yS4" id="vad-hk-wue"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="x5A-6p-PRh" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="142" y="134"/>
+        </scene>
+    </scenes>
+</document>

+ 38 - 0
Example-iOS/Images.xcassets/AppIcon.appiconset/Contents.json

@@ -0,0 +1,38 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

+ 40 - 0
Example-iOS/Info.plist

@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIMainStoryboardFile</key>
+	<string>Main</string>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>armv7</string>
+	</array>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+</dict>
+</plist>

+ 45 - 0
Example-iOS/ViewController.swift

@@ -0,0 +1,45 @@
+//
+//  ViewController.swift
+//  Example-iOS
+//
+//  Created by Indragie on 1/15/15.
+//  Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
+//
+
+import UIKit
+import CocoaMarkdown
+
+class ViewController: UIViewController {
+    @IBOutlet var textView: UITextView!
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        let path = Bundle.main.path(forResource: "test", ofType: "md")!
+        let document = CMDocument(contentsOfFile: path, options: CMDocumentOptions(rawValue: 0))
+        
+        let textAttributes = CMTextAttributes()
+        
+        // Customize the color and font of header elements
+        textAttributes.addStringAttributes([ .foregroundColor: UIColor(red: 0.0, green: 0.446, blue: 0.657, alpha: 1.0)], forElementWithKinds: .anyHeader)
+        let boldItalicTrait: UIFontDescriptor.SymbolicTraits = [.traitBold, .traitItalic]
+        textAttributes.addFontAttributes([ .family: "Avenir Next" ,
+                                           .traits: [ UIFontDescriptor.TraitKey.symbolic: boldItalicTrait.rawValue]], 
+                                         forElementWithKinds: .anyHeader)
+        textAttributes.setFontTraits([.weight: UIFont.Weight.heavy], forElementWithKinds: [.header1, .header2])
+        textAttributes.addFontAttributes([ .size: 40 ], forElementWithKinds: .header1)
+        
+        // Customize the font and paragraph alignment of block-quotes
+        textAttributes.addFontAttributes([.family: "Noteworthy", .size: 18], forElementWithKinds: .blockQuote)
+        textAttributes.addParagraphStyleAttributes([ .alignment: NSTextAlignment.center.rawValue], forElementWithKinds: .blockQuote)
+        
+        // Customize the background color of code elements
+        textAttributes.addStringAttributes([ .backgroundColor: UIColor(white: 0.9, alpha: 0.5)], forElementWithKinds: [.inlineCode, .codeBlock])
+        
+        let renderer = CMAttributedStringRenderer(document: document, attributes: textAttributes)!
+        renderer.register(CMHTMLStrikethroughTransformer())
+        renderer.register(CMHTMLSuperscriptTransformer())
+        renderer.register(CMHTMLUnderlineTransformer())
+        textView.attributedText = renderer.render()
+    }
+}
+

+ 19 - 0
LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2014 Indragie Karunaratne
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 191 - 0
README.md

@@ -0,0 +1,191 @@
+## 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](https://github.com/jgm/CommonMark) of [CommonMark](http://commonmark.org).
+
+### 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](images/example-app-mac.png)
+
+![Example app iOS](images/example-app-iOS.png)
+
+### 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`](CocoaMarkdown/CMNode.h) and [`CMIterator`](CocoaMarkdown/CMIterator.h) wrap CommonMark's C types with an object-oriented interface for traversal of the Markdown AST.
+
+```swift
+let document = CMDocument(contentsOfFile: path, options: [])
+document.rootNode.iterator().enumerateUsingBlock { (node, _, _) in
+    print("String value: \(node.stringValue)")
+}
+```
+
+#### Building Custom Renderers
+
+The [`CMParser`](CocoaMarkdown/CMParser.h) 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:
+
+```objective-c
+@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`](CocoaMarkdown/CMAttributedStringRenderer.h) is an example of a custom renderer that is built using this API.
+
+#### Rendering Attributed Strings
+
+[`CMAttributedStringRenderer`](CocoaMarkdown/CMAttributedStringRenderer.h) 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:
+
+```swift
+let document = CMDocument(contentsOfFile: path, options: [])
+let renderer = CMAttributedStringRenderer(document: document, attributes: CMTextAttributes())
+textView.attributedText = renderer.render()
+```
+
+Or, using the convenience method on `CMDocument`:
+
+```swift
+textView.attributedText = CMDocument(contentsOfFile: path, options: []).attributedStringWithAttributes(CMTextAttributes())
+```
+
+HTML elements can be supported by implementing [`CMHTMLElementTransformer`](CocoaMarkdown/CMHTMLElementTransformer.h). The framework includes several transformers for commonly used tags:
+
+* [`CMHTMLStrikethroughTransformer`](CocoaMarkdown/CMHTMLStrikethroughTransformer.h)
+* [`CMHTMLSuperscriptTransformer`](CocoaMarkdown/CMHTMLSuperscriptTransformer.h)
+* [`CMHTMLSubscriptTransformer`](CocoaMarkdown/CMHTMLSubscriptTransformer.h)
+
+Transformers can be registered with the renderer to use them:
+
+```swift
+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`](CocoaMarkdown/CMTextAttributes.h) 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:
+
+```swift
+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`:
+
+```swift
+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:
+
+```swift
+// 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`](CocoaMarkdown/CMHTMLRenderer.h) provides the ability to render HTML from Markdown:
+
+```swift
+let document = CMDocument(contentsOfFile: path, options: [])
+let renderer = CMHTMLRenderer(document: document)
+let HTML = renderer.render()
+```
+
+Or, using the convenience method on `CMDocument`:
+
+```swift
+let HTML = CMDocument(contentsOfFile: path).HTMLString()
+```
+
+### Example Apps
+
+The project includes example apps for iOS and macOS to demonstrate rendering attributed strings.
+
+### Contact
+
+* Indragie Karunaratne
+* [@indragie](http://twitter.com/indragie)
+* [http://indragie.com](http://indragie.com)
+
+### License
+
+CocoaMarkdown is licensed under the MIT License. See `LICENSE` for more information.

BIN
images/example-app-iOS.png


BIN
images/example-app-mac.png