react-native-enriched-markdown 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +479 -0
  3. package/ReactNativeEnrichedMarkdown.podspec +27 -0
  4. package/android/build.gradle +101 -0
  5. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +39 -0
  6. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +21 -0
  7. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ComponentDescriptors.cpp +22 -0
  8. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ComponentDescriptors.h +24 -0
  9. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +24 -0
  10. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +25 -0
  11. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +57 -0
  12. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +1164 -0
  13. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ShadowNodes.cpp +17 -0
  14. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ShadowNodes.h +32 -0
  15. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/States.cpp +16 -0
  16. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/States.h +20 -0
  17. package/android/gradle.properties +5 -0
  18. package/android/src/main/AndroidManifest.xml +2 -0
  19. package/android/src/main/baseline-prof.txt +65 -0
  20. package/android/src/main/cpp/jni-adapter.cpp +203 -0
  21. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +153 -0
  22. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextLayoutManager.kt +30 -0
  23. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +119 -0
  24. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextPackage.kt +17 -0
  25. package/android/src/main/java/com/swmansion/enriched/markdown/MeasurementStore.kt +165 -0
  26. package/android/src/main/java/com/swmansion/enriched/markdown/events/LinkPressEvent.kt +23 -0
  27. package/android/src/main/java/com/swmansion/enriched/markdown/parser/MarkdownASTNode.kt +29 -0
  28. package/android/src/main/java/com/swmansion/enriched/markdown/parser/Parser.kt +48 -0
  29. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockStyleContext.kt +166 -0
  30. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockquoteRenderer.kt +89 -0
  31. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeBlockRenderer.kt +105 -0
  32. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeRenderer.kt +35 -0
  33. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/DocumentRenderer.kt +15 -0
  34. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/EmphasisRenderer.kt +26 -0
  35. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +54 -0
  36. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ImageRenderer.kt +52 -0
  37. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LineBreakRenderer.kt +15 -0
  38. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LinkRenderer.kt +28 -0
  39. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListContextManager.kt +105 -0
  40. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListItemRenderer.kt +58 -0
  41. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt +69 -0
  42. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/NodeRenderer.kt +99 -0
  43. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ParagraphRenderer.kt +66 -0
  44. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/Renderer.kt +95 -0
  45. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +85 -0
  46. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/StrongRenderer.kt +26 -0
  47. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/TextRenderer.kt +29 -0
  48. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ThematicBreakRenderer.kt +44 -0
  49. package/android/src/main/java/com/swmansion/enriched/markdown/spans/BaseListSpan.kt +136 -0
  50. package/android/src/main/java/com/swmansion/enriched/markdown/spans/BlockquoteSpan.kt +135 -0
  51. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBackgroundSpan.kt +180 -0
  52. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBlockSpan.kt +196 -0
  53. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeSpan.kt +27 -0
  54. package/android/src/main/java/com/swmansion/enriched/markdown/spans/EmphasisSpan.kt +34 -0
  55. package/android/src/main/java/com/swmansion/enriched/markdown/spans/HeadingSpan.kt +38 -0
  56. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ImageSpan.kt +320 -0
  57. package/android/src/main/java/com/swmansion/enriched/markdown/spans/LineHeightSpan.kt +36 -0
  58. package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkSpan.kt +37 -0
  59. package/android/src/main/java/com/swmansion/enriched/markdown/spans/MarginBottomSpan.kt +76 -0
  60. package/android/src/main/java/com/swmansion/enriched/markdown/spans/OrderedListSpan.kt +87 -0
  61. package/android/src/main/java/com/swmansion/enriched/markdown/spans/StrongSpan.kt +37 -0
  62. package/android/src/main/java/com/swmansion/enriched/markdown/spans/TextSpan.kt +26 -0
  63. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ThematicBreakSpan.kt +69 -0
  64. package/android/src/main/java/com/swmansion/enriched/markdown/spans/UnorderedListSpan.kt +69 -0
  65. package/android/src/main/java/com/swmansion/enriched/markdown/styles/BaseBlockStyle.kt +10 -0
  66. package/android/src/main/java/com/swmansion/enriched/markdown/styles/BlockquoteStyle.kt +48 -0
  67. package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeBlockStyle.kt +51 -0
  68. package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeStyle.kt +21 -0
  69. package/android/src/main/java/com/swmansion/enriched/markdown/styles/EmphasisStyle.kt +17 -0
  70. package/android/src/main/java/com/swmansion/enriched/markdown/styles/HeadingStyle.kt +29 -0
  71. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ImageStyle.kt +21 -0
  72. package/android/src/main/java/com/swmansion/enriched/markdown/styles/InlineImageStyle.kt +17 -0
  73. package/android/src/main/java/com/swmansion/enriched/markdown/styles/LinkStyle.kt +19 -0
  74. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ListStyle.kt +54 -0
  75. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ParagraphStyle.kt +29 -0
  76. package/android/src/main/java/com/swmansion/enriched/markdown/styles/StrongStyle.kt +17 -0
  77. package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleConfig.kt +180 -0
  78. package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleParser.kt +75 -0
  79. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ThematicBreakStyle.kt +23 -0
  80. package/android/src/main/java/com/swmansion/enriched/markdown/utils/AsyncDrawable.kt +91 -0
  81. package/android/src/main/java/com/swmansion/enriched/markdown/utils/HTMLGenerator.kt +809 -0
  82. package/android/src/main/java/com/swmansion/enriched/markdown/utils/MarkdownExtractor.kt +365 -0
  83. package/android/src/main/java/com/swmansion/enriched/markdown/utils/SelectionActionMode.kt +139 -0
  84. package/android/src/main/java/com/swmansion/enriched/markdown/utils/Utils.kt +181 -0
  85. package/android/src/main/jni/CMakeLists.txt +82 -0
  86. package/android/src/main/jni/EnrichedMarkdownTextSpec.cpp +21 -0
  87. package/android/src/main/jni/EnrichedMarkdownTextSpec.h +25 -0
  88. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextComponentDescriptor.h +29 -0
  89. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextMeasurementManager.cpp +45 -0
  90. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextMeasurementManager.h +21 -0
  91. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.cpp +33 -0
  92. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.h +49 -0
  93. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.cpp +9 -0
  94. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.h +25 -0
  95. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/conversions.h +19 -0
  96. package/cpp/md4c/md4c.c +6492 -0
  97. package/cpp/md4c/md4c.h +402 -0
  98. package/cpp/parser/MD4CParser.cpp +314 -0
  99. package/cpp/parser/MD4CParser.hpp +23 -0
  100. package/cpp/parser/MarkdownASTNode.hpp +49 -0
  101. package/ios/EnrichedMarkdownText.h +18 -0
  102. package/ios/EnrichedMarkdownText.mm +1074 -0
  103. package/ios/attachments/ImageAttachment.h +23 -0
  104. package/ios/attachments/ImageAttachment.m +185 -0
  105. package/ios/attachments/ThematicBreakAttachment.h +15 -0
  106. package/ios/attachments/ThematicBreakAttachment.m +33 -0
  107. package/ios/generated/EnrichedMarkdownTextSpec/ComponentDescriptors.cpp +22 -0
  108. package/ios/generated/EnrichedMarkdownTextSpec/ComponentDescriptors.h +24 -0
  109. package/ios/generated/EnrichedMarkdownTextSpec/EventEmitters.cpp +24 -0
  110. package/ios/generated/EnrichedMarkdownTextSpec/EventEmitters.h +25 -0
  111. package/ios/generated/EnrichedMarkdownTextSpec/Props.cpp +57 -0
  112. package/ios/generated/EnrichedMarkdownTextSpec/Props.h +1164 -0
  113. package/ios/generated/EnrichedMarkdownTextSpec/RCTComponentViewHelpers.h +20 -0
  114. package/ios/generated/EnrichedMarkdownTextSpec/ShadowNodes.cpp +17 -0
  115. package/ios/generated/EnrichedMarkdownTextSpec/ShadowNodes.h +32 -0
  116. package/ios/generated/EnrichedMarkdownTextSpec/States.cpp +16 -0
  117. package/ios/generated/EnrichedMarkdownTextSpec/States.h +20 -0
  118. package/ios/internals/EnrichedMarkdownTextComponentDescriptor.h +19 -0
  119. package/ios/internals/EnrichedMarkdownTextShadowNode.h +43 -0
  120. package/ios/internals/EnrichedMarkdownTextShadowNode.mm +85 -0
  121. package/ios/internals/EnrichedMarkdownTextState.h +24 -0
  122. package/ios/parser/MarkdownASTNode.h +33 -0
  123. package/ios/parser/MarkdownASTNode.m +32 -0
  124. package/ios/parser/MarkdownParser.h +8 -0
  125. package/ios/parser/MarkdownParser.mm +13 -0
  126. package/ios/parser/MarkdownParserBridge.mm +110 -0
  127. package/ios/renderer/AttributedRenderer.h +9 -0
  128. package/ios/renderer/AttributedRenderer.m +119 -0
  129. package/ios/renderer/BlockquoteRenderer.h +7 -0
  130. package/ios/renderer/BlockquoteRenderer.m +159 -0
  131. package/ios/renderer/CodeBlockRenderer.h +10 -0
  132. package/ios/renderer/CodeBlockRenderer.m +89 -0
  133. package/ios/renderer/CodeRenderer.h +10 -0
  134. package/ios/renderer/CodeRenderer.m +60 -0
  135. package/ios/renderer/EmphasisRenderer.h +6 -0
  136. package/ios/renderer/EmphasisRenderer.m +96 -0
  137. package/ios/renderer/HeadingRenderer.h +7 -0
  138. package/ios/renderer/HeadingRenderer.m +98 -0
  139. package/ios/renderer/ImageRenderer.h +12 -0
  140. package/ios/renderer/ImageRenderer.m +62 -0
  141. package/ios/renderer/LinkRenderer.h +7 -0
  142. package/ios/renderer/LinkRenderer.m +69 -0
  143. package/ios/renderer/ListItemRenderer.h +16 -0
  144. package/ios/renderer/ListItemRenderer.m +91 -0
  145. package/ios/renderer/ListRenderer.h +13 -0
  146. package/ios/renderer/ListRenderer.m +67 -0
  147. package/ios/renderer/NodeRenderer.h +8 -0
  148. package/ios/renderer/ParagraphRenderer.h +7 -0
  149. package/ios/renderer/ParagraphRenderer.m +69 -0
  150. package/ios/renderer/RenderContext.h +88 -0
  151. package/ios/renderer/RenderContext.m +248 -0
  152. package/ios/renderer/RendererFactory.h +12 -0
  153. package/ios/renderer/RendererFactory.m +110 -0
  154. package/ios/renderer/StrongRenderer.h +6 -0
  155. package/ios/renderer/StrongRenderer.m +83 -0
  156. package/ios/renderer/TextRenderer.h +6 -0
  157. package/ios/renderer/TextRenderer.m +16 -0
  158. package/ios/renderer/ThematicBreakRenderer.h +5 -0
  159. package/ios/renderer/ThematicBreakRenderer.m +53 -0
  160. package/ios/styles/StyleConfig.h +228 -0
  161. package/ios/styles/StyleConfig.mm +1467 -0
  162. package/ios/utils/BlockquoteBorder.h +20 -0
  163. package/ios/utils/BlockquoteBorder.m +92 -0
  164. package/ios/utils/CodeBackground.h +19 -0
  165. package/ios/utils/CodeBackground.m +191 -0
  166. package/ios/utils/CodeBlockBackground.h +17 -0
  167. package/ios/utils/CodeBlockBackground.m +87 -0
  168. package/ios/utils/EditMenuUtils.h +22 -0
  169. package/ios/utils/EditMenuUtils.m +118 -0
  170. package/ios/utils/FontUtils.h +20 -0
  171. package/ios/utils/FontUtils.m +13 -0
  172. package/ios/utils/HTMLGenerator.h +20 -0
  173. package/ios/utils/HTMLGenerator.m +779 -0
  174. package/ios/utils/LastElementUtils.h +53 -0
  175. package/ios/utils/ListMarkerDrawer.h +15 -0
  176. package/ios/utils/ListMarkerDrawer.m +127 -0
  177. package/ios/utils/MarkdownExtractor.h +17 -0
  178. package/ios/utils/MarkdownExtractor.m +295 -0
  179. package/ios/utils/ParagraphStyleUtils.h +13 -0
  180. package/ios/utils/ParagraphStyleUtils.m +56 -0
  181. package/ios/utils/PasteboardUtils.h +36 -0
  182. package/ios/utils/PasteboardUtils.m +134 -0
  183. package/ios/utils/RTFExportUtils.h +24 -0
  184. package/ios/utils/RTFExportUtils.m +297 -0
  185. package/ios/utils/RuntimeKeys.h +38 -0
  186. package/ios/utils/RuntimeKeys.m +11 -0
  187. package/ios/utils/TextViewLayoutManager.h +14 -0
  188. package/ios/utils/TextViewLayoutManager.mm +113 -0
  189. package/lib/module/EnrichedMarkdownText.js +34 -0
  190. package/lib/module/EnrichedMarkdownText.js.map +1 -0
  191. package/lib/module/EnrichedMarkdownTextNativeComponent.ts +130 -0
  192. package/lib/module/index.js +5 -0
  193. package/lib/module/index.js.map +1 -0
  194. package/lib/module/normalizeMarkdownStyle.js +340 -0
  195. package/lib/module/normalizeMarkdownStyle.js.map +1 -0
  196. package/lib/module/package.json +1 -0
  197. package/lib/typescript/package.json +1 -0
  198. package/lib/typescript/src/EnrichedMarkdownText.d.ts +101 -0
  199. package/lib/typescript/src/EnrichedMarkdownText.d.ts.map +1 -0
  200. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +111 -0
  201. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -0
  202. package/lib/typescript/src/index.d.ts +5 -0
  203. package/lib/typescript/src/index.d.ts.map +1 -0
  204. package/lib/typescript/src/normalizeMarkdownStyle.d.ts +6 -0
  205. package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -0
  206. package/package.json +186 -1
  207. package/react-native.config.js +13 -0
  208. package/src/EnrichedMarkdownText.tsx +152 -0
  209. package/src/EnrichedMarkdownTextNativeComponent.ts +130 -0
  210. package/src/index.tsx +7 -0
  211. package/src/normalizeMarkdownStyle.ts +377 -0
@@ -0,0 +1,159 @@
1
+ #import "BlockquoteRenderer.h"
2
+ #import "BlockquoteBorder.h"
3
+ #import "FontUtils.h"
4
+ #import "MarkdownASTNode.h"
5
+ #import "ParagraphStyleUtils.h"
6
+ #import "RendererFactory.h"
7
+ #import "StyleConfig.h"
8
+
9
+ static NSString *const kNestedInfoDepthKey = @"depth";
10
+ static NSString *const kNestedInfoRangeKey = @"range";
11
+
12
+ @implementation BlockquoteRenderer {
13
+ RendererFactory *_rendererFactory;
14
+ StyleConfig *_config;
15
+ }
16
+
17
+ - (instancetype)initWithRendererFactory:(id)rendererFactory config:(id)config
18
+ {
19
+ if (self = [super init]) {
20
+ _rendererFactory = rendererFactory;
21
+ _config = (StyleConfig *)config;
22
+ }
23
+ return self;
24
+ }
25
+
26
+ - (void)renderNode:(MarkdownASTNode *)node into:(NSMutableAttributedString *)output context:(RenderContext *)context
27
+ {
28
+ NSInteger currentDepth = context.blockquoteDepth;
29
+ context.blockquoteDepth = currentDepth + 1;
30
+
31
+ [context setBlockStyle:BlockTypeBlockquote
32
+ font:[_config blockquoteFont]
33
+ color:[_config blockquoteColor]
34
+ headingLevel:0];
35
+
36
+ NSUInteger start = output.length;
37
+ @try {
38
+ [_rendererFactory renderChildrenOfNode:node into:output context:context];
39
+ } @finally {
40
+ [context clearBlockStyle];
41
+ context.blockquoteDepth = currentDepth;
42
+ }
43
+
44
+ NSUInteger end = output.length;
45
+ if (end <= start) {
46
+ return;
47
+ }
48
+
49
+ [self applyStylingAndSpacing:output start:start end:end currentDepth:currentDepth];
50
+ }
51
+
52
+ #pragma mark - Styling and Spacing
53
+
54
+ - (void)applyStylingAndSpacing:(NSMutableAttributedString *)output
55
+ start:(NSUInteger)start
56
+ end:(NSUInteger)end
57
+ currentDepth:(NSInteger)currentDepth
58
+ {
59
+ NSRange blockquoteRange = NSMakeRange(start, end - start);
60
+ CGFloat levelSpacing = [_config blockquoteBorderWidth] + [_config blockquoteGapWidth];
61
+ NSArray<NSDictionary *> *nestedInfo = [self collectNestedBlockquotes:output range:blockquoteRange depth:currentDepth];
62
+
63
+ // Apply base styling (indentation, depth, background, line height)
64
+ [self applyBaseBlockquoteStyle:output
65
+ range:blockquoteRange
66
+ depth:currentDepth
67
+ levelSpacing:levelSpacing
68
+ backgroundColor:[_config blockquoteBackgroundColor]
69
+ lineHeight:[_config blockquoteLineHeight]];
70
+
71
+ // Re-apply nested blockquote styles to restore their correct indentation
72
+ // (applyBaseBlockquoteStyle overwrites nested indents with the parent's indent)
73
+ [self reapplyNestedStyles:output nestedInfo:nestedInfo levelSpacing:levelSpacing];
74
+
75
+ // Apply bottom margin for top-level blockquotes only
76
+ if (currentDepth == 0) {
77
+ CGFloat marginBottom = [_config blockquoteMarginBottom];
78
+ if (marginBottom > 0) {
79
+ applyBlockSpacing(output, marginBottom);
80
+ }
81
+ }
82
+ }
83
+
84
+ #pragma mark - Nested Blockquote Handling
85
+
86
+ - (NSArray<NSDictionary *> *)collectNestedBlockquotes:(NSMutableAttributedString *)output
87
+ range:(NSRange)blockquoteRange
88
+ depth:(NSInteger)currentDepth
89
+ {
90
+ NSMutableArray<NSDictionary *> *nestedInfo = [NSMutableArray array];
91
+
92
+ [output
93
+ enumerateAttribute:BlockquoteDepthAttributeName
94
+ inRange:blockquoteRange
95
+ options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
96
+ usingBlock:^(id value, NSRange range, BOOL *stop) {
97
+ NSInteger depth = [value integerValue];
98
+ if (value && depth > currentDepth) {
99
+ [nestedInfo
100
+ addObject:@{kNestedInfoDepthKey : value, kNestedInfoRangeKey : [NSValue valueWithRange:range]}];
101
+ }
102
+ }];
103
+
104
+ return nestedInfo;
105
+ }
106
+
107
+ - (void)applyBaseBlockquoteStyle:(NSMutableAttributedString *)output
108
+ range:(NSRange)blockquoteRange
109
+ depth:(NSInteger)currentDepth
110
+ levelSpacing:(CGFloat)levelSpacing
111
+ backgroundColor:(UIColor *)backgroundColor
112
+ lineHeight:(CGFloat)lineHeight
113
+ {
114
+ NSMutableParagraphStyle *paragraphStyle = getOrCreateParagraphStyle(output, blockquoteRange.location);
115
+ CGFloat totalIndent = [self calculateIndentForDepth:currentDepth levelSpacing:levelSpacing];
116
+ paragraphStyle.firstLineHeadIndent = totalIndent;
117
+ paragraphStyle.headIndent = totalIndent;
118
+
119
+ NSMutableDictionary *newAttributes =
120
+ [NSMutableDictionary dictionaryWithObjectsAndKeys:paragraphStyle, NSParagraphStyleAttributeName, @(currentDepth),
121
+ BlockquoteDepthAttributeName, nil];
122
+ if (backgroundColor) {
123
+ newAttributes[BlockquoteBackgroundColorAttributeName] = backgroundColor;
124
+ }
125
+ [output addAttributes:newAttributes range:blockquoteRange];
126
+
127
+ applyLineHeight(output, blockquoteRange, lineHeight);
128
+ }
129
+
130
+ - (void)reapplyNestedStyles:(NSMutableAttributedString *)output
131
+ nestedInfo:(NSArray<NSDictionary *> *)nestedInfo
132
+ levelSpacing:(CGFloat)levelSpacing
133
+ {
134
+ // Re-apply indentation to nested blockquotes since applyBaseBlockquoteStyle
135
+ // overwrote them with the parent's indentation
136
+ for (NSDictionary *info in nestedInfo) {
137
+ NSRange nestedRange = [info[kNestedInfoRangeKey] rangeValue];
138
+ NSInteger nestedDepth = [info[kNestedInfoDepthKey] integerValue];
139
+ NSMutableParagraphStyle *style = getOrCreateParagraphStyle(output, nestedRange.location);
140
+
141
+ CGFloat indent = [self calculateIndentForDepth:nestedDepth levelSpacing:levelSpacing];
142
+ style.firstLineHeadIndent = indent;
143
+ style.headIndent = indent;
144
+ style.tailIndent = 0;
145
+
146
+ [output
147
+ addAttributes:@{NSParagraphStyleAttributeName : style, BlockquoteDepthAttributeName : info[kNestedInfoDepthKey]}
148
+ range:nestedRange];
149
+ }
150
+ }
151
+
152
+ #pragma mark - Helper Methods
153
+
154
+ - (CGFloat)calculateIndentForDepth:(NSInteger)depth levelSpacing:(CGFloat)levelSpacing
155
+ {
156
+ return (depth + 1) * levelSpacing;
157
+ }
158
+
159
+ @end
@@ -0,0 +1,10 @@
1
+ #pragma once
2
+ #import "NodeRenderer.h"
3
+
4
+ NS_ASSUME_NONNULL_BEGIN
5
+
6
+ @interface CodeBlockRenderer : NSObject <NodeRenderer>
7
+ - (instancetype)initWithRendererFactory:(id)rendererFactory config:(id)config;
8
+ @end
9
+
10
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,89 @@
1
+ #import "CodeBlockRenderer.h"
2
+ #import "CodeBlockBackground.h"
3
+ #import "LastElementUtils.h"
4
+ #import "MarkdownASTNode.h"
5
+ #import "ParagraphStyleUtils.h"
6
+ #import "RenderContext.h"
7
+ #import "RendererFactory.h"
8
+ #import "StyleConfig.h"
9
+
10
+ @implementation CodeBlockRenderer {
11
+ RendererFactory *_rendererFactory;
12
+ StyleConfig *_config;
13
+ }
14
+
15
+ - (instancetype)initWithRendererFactory:(id)rendererFactory config:(id)config
16
+ {
17
+ if (self = [super init]) {
18
+ _rendererFactory = rendererFactory;
19
+ _config = (StyleConfig *)config;
20
+ }
21
+ return self;
22
+ }
23
+
24
+ - (void)renderNode:(MarkdownASTNode *)node into:(NSMutableAttributedString *)output context:(RenderContext *)context
25
+ {
26
+ [context setBlockStyle:BlockTypeCodeBlock font:[_config codeBlockFont] color:[_config codeBlockColor] headingLevel:0];
27
+
28
+ CGFloat padding = [_config codeBlockPadding];
29
+ CGFloat lineHeight = [_config codeBlockLineHeight];
30
+ CGFloat marginBottom = [_config codeBlockMarginBottom];
31
+ NSUInteger blockStart = output.length;
32
+
33
+ // 1. TOP PADDING: Inside the background
34
+ [output appendAttributedString:kNewlineAttributedString];
35
+ NSMutableParagraphStyle *topSpacerStyle = [context spacerStyleWithHeight:padding spacing:0];
36
+ [output addAttribute:NSParagraphStyleAttributeName value:topSpacerStyle range:NSMakeRange(blockStart, 1)];
37
+
38
+ // 2. RENDER CONTENT
39
+ NSUInteger contentStart = output.length;
40
+ @try {
41
+ [_rendererFactory renderChildrenOfNode:node into:output context:context];
42
+ } @finally {
43
+ [context clearBlockStyle];
44
+ }
45
+
46
+ NSUInteger contentEnd = output.length;
47
+ if (contentEnd <= contentStart)
48
+ return;
49
+
50
+ NSRange contentRange = NSMakeRange(contentStart, contentEnd - contentStart);
51
+
52
+ // 3. CONTENT STYLING
53
+ UIFont *codeFont = [_config codeBlockFont];
54
+ UIColor *codeColor = [_config codeBlockColor];
55
+ if (codeColor) {
56
+ [output addAttributes:@{NSFontAttributeName : codeFont, NSForegroundColorAttributeName : codeColor}
57
+ range:contentRange];
58
+ } else {
59
+ [output addAttribute:NSFontAttributeName value:codeFont range:contentRange];
60
+ }
61
+
62
+ if (lineHeight > 0) {
63
+ applyLineHeight(output, contentRange, lineHeight);
64
+ }
65
+
66
+ // HORIZONTAL INDENTS
67
+ NSMutableParagraphStyle *baseStyle = [getOrCreateParagraphStyle(output, contentStart) mutableCopy];
68
+ baseStyle.firstLineHeadIndent = padding;
69
+ baseStyle.headIndent = padding;
70
+ baseStyle.tailIndent = -padding;
71
+ [output addAttribute:NSParagraphStyleAttributeName value:baseStyle range:contentRange];
72
+
73
+ // 4. BOTTOM PADDING: Inside the background
74
+ NSUInteger bottomPaddingStart = output.length;
75
+ [output appendAttributedString:kNewlineAttributedString];
76
+ NSMutableParagraphStyle *bottomPaddingStyle = [context spacerStyleWithHeight:padding spacing:0];
77
+ [output addAttribute:NSParagraphStyleAttributeName value:bottomPaddingStyle range:NSMakeRange(bottomPaddingStart, 1)];
78
+
79
+ // MARK BACKGROUND: Ends here to exclude the margin
80
+ NSRange backgroundRange = NSMakeRange(blockStart, output.length - blockStart);
81
+ [output addAttribute:CodeBlockAttributeName value:@YES range:backgroundRange];
82
+
83
+ // 5. EXTERNAL MARGIN: Outside the background
84
+ if (marginBottom > 0) {
85
+ applyBlockSpacing(output, marginBottom);
86
+ }
87
+ }
88
+
89
+ @end
@@ -0,0 +1,10 @@
1
+ #pragma once
2
+ #import "NodeRenderer.h"
3
+
4
+ NS_ASSUME_NONNULL_BEGIN
5
+
6
+ @interface CodeRenderer : NSObject <NodeRenderer>
7
+ - (instancetype)initWithRendererFactory:(id)rendererFactory config:(id)config;
8
+ @end
9
+
10
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,60 @@
1
+ #import "CodeRenderer.h"
2
+ #import "CodeBackground.h"
3
+ #import "FontUtils.h"
4
+ #import "MarkdownASTNode.h"
5
+ #import "RenderContext.h"
6
+ #import "RendererFactory.h"
7
+ #import "StyleConfig.h"
8
+
9
+ @implementation CodeRenderer {
10
+ RendererFactory *_rendererFactory;
11
+ StyleConfig *_config;
12
+ }
13
+
14
+ - (instancetype)initWithRendererFactory:(id)rendererFactory config:(id)config
15
+ {
16
+ self = [super init];
17
+ if (self) {
18
+ _rendererFactory = rendererFactory;
19
+ _config = (StyleConfig *)config;
20
+ }
21
+ return self;
22
+ }
23
+
24
+ - (void)renderNode:(MarkdownASTNode *)node into:(NSMutableAttributedString *)output context:(RenderContext *)context
25
+ {
26
+
27
+ BlockStyle *blockStyle = [context getBlockStyle];
28
+
29
+ UIColor *codeColor = _config.codeColor;
30
+
31
+ UIFont *blockFont = cachedFontFromBlockStyle(blockStyle, context);
32
+
33
+ UIFontDescriptorSymbolicTraits traits = blockFont.fontDescriptor.symbolicTraits;
34
+ UIFontWeight weight = (traits & UIFontDescriptorTraitBold) ? UIFontWeightBold : UIFontWeightRegular;
35
+
36
+ UIFont *monospacedFont = [UIFont monospacedSystemFontOfSize:blockStyle.fontSize weight:weight];
37
+
38
+ NSUInteger start = output.length;
39
+
40
+ [_rendererFactory renderChildrenOfNode:node into:output context:context];
41
+
42
+ NSRange range = [RenderContext rangeForRenderedContent:output start:start];
43
+ if (range.length > 0) {
44
+ NSDictionary *existingAttributes = [output attributesAtIndex:start effectiveRange:NULL];
45
+ NSMutableDictionary *codeAttributes = [existingAttributes ?: @{} mutableCopy];
46
+
47
+ codeAttributes[NSFontAttributeName] = monospacedFont;
48
+ if (codeColor) {
49
+ codeAttributes[NSForegroundColorAttributeName] = codeColor;
50
+ }
51
+ codeAttributes[CodeAttributeName] = @YES;
52
+
53
+ // Store block line height directly for CodeBackground to use
54
+ codeAttributes[@"BlockLineHeight"] = @(blockFont.lineHeight);
55
+
56
+ [output setAttributes:codeAttributes range:range];
57
+ }
58
+ }
59
+
60
+ @end
@@ -0,0 +1,6 @@
1
+ #pragma once
2
+ #import "NodeRenderer.h"
3
+
4
+ @interface EmphasisRenderer : NSObject <NodeRenderer>
5
+ - (instancetype)initWithRendererFactory:(id)rendererFactory config:(id)config;
6
+ @end
@@ -0,0 +1,96 @@
1
+ #import "EmphasisRenderer.h"
2
+ #import "FontUtils.h"
3
+ #import "MarkdownASTNode.h"
4
+ #import "RenderContext.h"
5
+ #import "RendererFactory.h"
6
+ #import "StyleConfig.h"
7
+
8
+ @implementation EmphasisRenderer {
9
+ RendererFactory *_rendererFactory;
10
+ StyleConfig *_config;
11
+ }
12
+
13
+ - (instancetype)initWithRendererFactory:(id)rendererFactory config:(id)config
14
+ {
15
+ self = [super init];
16
+ if (self) {
17
+ _rendererFactory = rendererFactory;
18
+ _config = (StyleConfig *)config;
19
+ }
20
+ return self;
21
+ }
22
+
23
+ #pragma mark - Rendering
24
+
25
+ - (void)renderNode:(MarkdownASTNode *)node into:(NSMutableAttributedString *)output context:(RenderContext *)context
26
+ {
27
+ NSUInteger start = output.length;
28
+ [_rendererFactory renderChildrenOfNode:node into:output context:context];
29
+
30
+ NSRange range = NSMakeRange(start, output.length - start);
31
+ if (range.length == 0)
32
+ return;
33
+
34
+ BlockStyle *blockStyle = [context getBlockStyle];
35
+ UIColor *configEmphasisColor = [_config emphasisColor];
36
+
37
+ // Cache the Strong color calculation to efficiently detect nested Strong nodes
38
+ UIColor *strongColorToPreserve = [_config strongColor] ? [RenderContext calculateStrongColor:[_config strongColor]
39
+ blockColor:blockStyle.color]
40
+ : nil;
41
+
42
+ [output
43
+ enumerateAttributesInRange:range
44
+ options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
45
+ usingBlock:^(NSDictionary<NSAttributedStringKey, id> *attrs, NSRange subrange, BOOL *stop) {
46
+ // 1. Font Optimization: Only apply italic trait if not already present
47
+ UIFont *currentFont =
48
+ attrs[NSFontAttributeName] ?: cachedFontFromBlockStyle(blockStyle, context);
49
+ if (currentFont && !(currentFont.fontDescriptor.symbolicTraits & UIFontDescriptorTraitItalic)) {
50
+ UIFont *italicFont = [self ensureFontIsItalic:currentFont];
51
+ if (italicFont) {
52
+ [output addAttribute:NSFontAttributeName value:italicFont range:subrange];
53
+ }
54
+ }
55
+
56
+ // 2. Color Optimization: Handle nesting and avoid redundant updates
57
+ if (configEmphasisColor) {
58
+ UIColor *currentColor = attrs[NSForegroundColorAttributeName];
59
+ BOOL isLink = attrs[NSLinkAttributeName] != nil;
60
+
61
+ // Verify if the current color belongs to a Strong parent
62
+ BOOL isStrongColor = strongColorToPreserve && [currentColor isEqual:strongColorToPreserve];
63
+
64
+ // Preserving colors for higher-priority elements (links, strong nodes, etc.)
65
+ if (!isLink && !isStrongColor && ![RenderContext shouldPreserveColors:attrs]) {
66
+ // Only modify the string if the color is actually different
67
+ if (![currentColor isEqual:configEmphasisColor]) {
68
+ [output addAttribute:NSForegroundColorAttributeName
69
+ value:configEmphasisColor
70
+ range:subrange];
71
+ }
72
+ }
73
+ }
74
+ }];
75
+ }
76
+
77
+ #pragma mark - Helper Methods
78
+
79
+ - (UIFont *)ensureFontIsItalic:(UIFont *)font
80
+ {
81
+ if (!font)
82
+ return nil;
83
+
84
+ UIFontDescriptorSymbolicTraits traits = font.fontDescriptor.symbolicTraits;
85
+ if (traits & UIFontDescriptorTraitItalic)
86
+ return font;
87
+
88
+ // Combine italic with existing traits (e.g., preserving Bold if present)
89
+ UIFontDescriptorSymbolicTraits combinedTraits = traits | UIFontDescriptorTraitItalic;
90
+ UIFontDescriptor *italicDescriptor = [font.fontDescriptor fontDescriptorWithSymbolicTraits:combinedTraits];
91
+
92
+ // Size 0 in fontWithDescriptor:size: maintains the current point size
93
+ return [UIFont fontWithDescriptor:italicDescriptor size:0] ?: font;
94
+ }
95
+
96
+ @end
@@ -0,0 +1,7 @@
1
+ #import "MarkdownASTNode.h"
2
+ #import "NodeRenderer.h"
3
+ #import "RenderContext.h"
4
+
5
+ @interface HeadingRenderer : NSObject <NodeRenderer>
6
+ - (instancetype)initWithRendererFactory:(id)rendererFactory config:(id)config;
7
+ @end
@@ -0,0 +1,98 @@
1
+ #import "HeadingRenderer.h"
2
+ #import "FontUtils.h"
3
+ #import "ParagraphStyleUtils.h"
4
+ #import "RenderContext.h"
5
+ #import "RendererFactory.h"
6
+ #import "RuntimeKeys.h"
7
+ #import "StyleConfig.h"
8
+
9
+ // Lightweight struct to hold style data without object overhead
10
+ typedef struct {
11
+ __unsafe_unretained UIFont *font;
12
+ __unsafe_unretained UIColor *color;
13
+ CGFloat marginBottom;
14
+ CGFloat lineHeight;
15
+ } HeadingStyle;
16
+
17
+ // Static heading type strings (index 0 unused, 1-6 for h1-h6)
18
+ static NSString *const kHeadingTypes[] = {nil, @"heading-1", @"heading-2", @"heading-3",
19
+ @"heading-4", @"heading-5", @"heading-6"};
20
+
21
+ @implementation HeadingRenderer {
22
+ RendererFactory *_rendererFactory;
23
+ StyleConfig *_config;
24
+ }
25
+
26
+ - (instancetype)initWithRendererFactory:(id)rendererFactory config:(id)config
27
+ {
28
+ if (self = [super init]) {
29
+ _rendererFactory = rendererFactory;
30
+ _config = (StyleConfig *)config;
31
+ }
32
+ return self;
33
+ }
34
+
35
+ #pragma mark - Rendering
36
+
37
+ - (void)renderNode:(MarkdownASTNode *)node into:(NSMutableAttributedString *)output context:(RenderContext *)context
38
+ {
39
+ NSInteger level = [node.attributes[@"level"] integerValue];
40
+ if (level < 1 || level > 6)
41
+ level = 1;
42
+
43
+ // Fetch style struct with pre-cached font from StyleConfig
44
+ HeadingStyle style = [self styleForLevel:level];
45
+
46
+ [context setBlockStyle:BlockTypeHeading font:style.font color:style.color headingLevel:level];
47
+
48
+ NSUInteger start = output.length;
49
+ @try {
50
+ [_rendererFactory renderChildrenOfNode:node into:output context:context];
51
+ } @finally {
52
+ [context clearBlockStyle];
53
+ }
54
+
55
+ NSRange range = NSMakeRange(start, output.length - start);
56
+ if (range.length == 0)
57
+ return;
58
+
59
+ // Mark as heading for HTML generation and Copy as Markdown
60
+ [output addAttribute:MarkdownTypeAttributeName value:kHeadingTypes[level] range:range];
61
+
62
+ applyLineHeight(output, range, style.lineHeight);
63
+ applyParagraphSpacing(output, start, style.marginBottom);
64
+ }
65
+
66
+ #pragma mark - Optimized Style Provider
67
+
68
+ - (HeadingStyle)styleForLevel:(NSInteger)level
69
+ {
70
+ StyleConfig *c = _config;
71
+ HeadingStyle s;
72
+
73
+ switch (level) {
74
+ case 1:
75
+ s = (HeadingStyle){c.h1Font, c.h1Color, c.h1MarginBottom, c.h1LineHeight};
76
+ break;
77
+ case 2:
78
+ s = (HeadingStyle){c.h2Font, c.h2Color, c.h2MarginBottom, c.h2LineHeight};
79
+ break;
80
+ case 3:
81
+ s = (HeadingStyle){c.h3Font, c.h3Color, c.h3MarginBottom, c.h3LineHeight};
82
+ break;
83
+ case 4:
84
+ s = (HeadingStyle){c.h4Font, c.h4Color, c.h4MarginBottom, c.h4LineHeight};
85
+ break;
86
+ case 5:
87
+ s = (HeadingStyle){c.h5Font, c.h5Color, c.h5MarginBottom, c.h5LineHeight};
88
+ break;
89
+ case 6:
90
+ s = (HeadingStyle){c.h6Font, c.h6Color, c.h6MarginBottom, c.h6LineHeight};
91
+ break;
92
+ default:
93
+ return [self styleForLevel:1];
94
+ }
95
+ return s;
96
+ }
97
+
98
+ @end
@@ -0,0 +1,12 @@
1
+ #pragma once
2
+ #import "NodeRenderer.h"
3
+
4
+ NS_ASSUME_NONNULL_BEGIN
5
+
6
+ @interface ImageRenderer : NSObject <NodeRenderer>
7
+
8
+ - (instancetype)initWithRendererFactory:(id)rendererFactory config:(id)config;
9
+
10
+ @end
11
+
12
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,62 @@
1
+ #import "ImageRenderer.h"
2
+ #import "ImageAttachment.h"
3
+ #import "MarkdownASTNode.h"
4
+ #import "RenderContext.h"
5
+ #import "RendererFactory.h"
6
+ #import "StyleConfig.h"
7
+
8
+ static const unichar kLineBreak = '\n';
9
+ static const unichar kZeroWidthSpace = 0x200B;
10
+
11
+ @implementation ImageRenderer {
12
+ RendererFactory *_rendererFactory;
13
+ StyleConfig *_config;
14
+ }
15
+
16
+ - (instancetype)initWithRendererFactory:(id)rendererFactory config:(id)config
17
+ {
18
+ if (self = [super init]) {
19
+ _rendererFactory = rendererFactory;
20
+ _config = (StyleConfig *)config;
21
+ }
22
+ return self;
23
+ }
24
+
25
+ #pragma mark - Rendering
26
+
27
+ - (void)renderNode:(MarkdownASTNode *)node into:(NSMutableAttributedString *)output context:(RenderContext *)context
28
+ {
29
+ NSString *imageURL = node.attributes[@"url"];
30
+ // Safety check for URL presence and length
31
+ if (!imageURL || imageURL.length == 0) {
32
+ return;
33
+ }
34
+
35
+ // Determine if this image is being placed inside an existing line of text
36
+ BOOL isInline = [self isInlineImageInOutput:output];
37
+
38
+ // Create the attachment using the shared config
39
+ ImageAttachment *attachment = [[ImageAttachment alloc] initWithImageURL:imageURL config:_config isInline:isInline];
40
+
41
+ // Append the attachment character to the output
42
+ NSAttributedString *imageString = [NSAttributedString attributedStringWithAttachment:attachment];
43
+ [output appendAttributedString:imageString];
44
+ }
45
+
46
+ #pragma mark - Private Helpers
47
+
48
+ - (BOOL)isInlineImageInOutput:(NSAttributedString *)output
49
+ {
50
+ if (output.length == 0) {
51
+ return NO;
52
+ }
53
+
54
+ // Check the last character to see if we are currently mid-paragraph
55
+ unichar lastChar = [output.string characterAtIndex:output.length - 1];
56
+
57
+ // If the last character is a newline or a zero-width space (often used as block separators),
58
+ // we consider the next image to be a "block" image.
59
+ return (lastChar != kLineBreak && lastChar != kZeroWidthSpace);
60
+ }
61
+
62
+ @end
@@ -0,0 +1,7 @@
1
+ #import "MarkdownASTNode.h"
2
+ #import "NodeRenderer.h"
3
+ #import "RenderContext.h"
4
+
5
+ @interface LinkRenderer : NSObject <NodeRenderer>
6
+ - (instancetype)initWithRendererFactory:(id)rendererFactory config:(id)config;
7
+ @end