@mathers101/react-native-render-rich-text 0.0.0-stage → 0.1.9

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 (358) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +140 -3
  3. package/ReactNativeRenderRichText.podspec +23 -0
  4. package/android/build.gradle +86 -0
  5. package/android/consumer-rules.pro +3 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/cpp/jni-adapter.cpp +191 -0
  8. package/android/src/main/java/com/renderrichtext/EnrichedTiptapText.kt +673 -0
  9. package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextLayoutManager.kt +24 -0
  10. package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextManager.kt +405 -0
  11. package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextPackage.kt +13 -0
  12. package/android/src/main/java/com/renderrichtext/MeasurementStore.kt +644 -0
  13. package/android/src/main/java/com/renderrichtext/accessibility/AccessibilityLabels.kt +17 -0
  14. package/android/src/main/java/com/renderrichtext/accessibility/AccessibleTiptapTextView.kt +112 -0
  15. package/android/src/main/java/com/renderrichtext/accessibility/TiptapAccessibilityHelper.kt +576 -0
  16. package/android/src/main/java/com/renderrichtext/events/CodeBlockPressEvent.kt +24 -0
  17. package/android/src/main/java/com/renderrichtext/events/ContextMenuItemPressEvent.kt +28 -0
  18. package/android/src/main/java/com/renderrichtext/events/CopyPressEvent.kt +24 -0
  19. package/android/src/main/java/com/renderrichtext/events/FactoidPressEvent.kt +23 -0
  20. package/android/src/main/java/com/renderrichtext/events/ImagePressEvent.kt +25 -0
  21. package/android/src/main/java/com/renderrichtext/events/LatexErrorEvent.kt +29 -0
  22. package/android/src/main/java/com/renderrichtext/events/LinkLongPressEvent.kt +23 -0
  23. package/android/src/main/java/com/renderrichtext/events/LinkPressEvent.kt +23 -0
  24. package/android/src/main/java/com/renderrichtext/events/MentionLongPressEvent.kt +25 -0
  25. package/android/src/main/java/com/renderrichtext/events/MentionPressEvent.kt +25 -0
  26. package/android/src/main/java/com/renderrichtext/events/TaskListItemPressEvent.kt +26 -0
  27. package/android/src/main/java/com/renderrichtext/math/LatexErrorReporter.kt +9 -0
  28. package/android/src/main/java/com/renderrichtext/parser/Parser.kt +59 -0
  29. package/android/src/main/java/com/renderrichtext/parser/TextLinkMatching.kt +35 -0
  30. package/android/src/main/java/com/renderrichtext/parser/TiptapASTNode.kt +69 -0
  31. package/android/src/main/java/com/renderrichtext/renderer/BaselineShiftRenderer.kt +45 -0
  32. package/android/src/main/java/com/renderrichtext/renderer/BlankLineRenderer.kt +39 -0
  33. package/android/src/main/java/com/renderrichtext/renderer/BlockStyleContext.kt +181 -0
  34. package/android/src/main/java/com/renderrichtext/renderer/BlockquoteRenderer.kt +243 -0
  35. package/android/src/main/java/com/renderrichtext/renderer/BlockquoteTextRenderer.kt +56 -0
  36. package/android/src/main/java/com/renderrichtext/renderer/CodeBlockRenderer.kt +135 -0
  37. package/android/src/main/java/com/renderrichtext/renderer/CodeRenderer.kt +37 -0
  38. package/android/src/main/java/com/renderrichtext/renderer/EmphasisRenderer.kt +28 -0
  39. package/android/src/main/java/com/renderrichtext/renderer/FactoidRenderer.kt +97 -0
  40. package/android/src/main/java/com/renderrichtext/renderer/HeadingRenderer.kt +81 -0
  41. package/android/src/main/java/com/renderrichtext/renderer/HighlightRenderer.kt +26 -0
  42. package/android/src/main/java/com/renderrichtext/renderer/LineBreakRenderer.kt +17 -0
  43. package/android/src/main/java/com/renderrichtext/renderer/LinkRenderer.kt +90 -0
  44. package/android/src/main/java/com/renderrichtext/renderer/ListContextManager.kt +109 -0
  45. package/android/src/main/java/com/renderrichtext/renderer/ListItemRenderer.kt +211 -0
  46. package/android/src/main/java/com/renderrichtext/renderer/ListRenderer.kt +133 -0
  47. package/android/src/main/java/com/renderrichtext/renderer/MentionRenderer.kt +85 -0
  48. package/android/src/main/java/com/renderrichtext/renderer/NodeRenderer.kt +207 -0
  49. package/android/src/main/java/com/renderrichtext/renderer/ParagraphRenderer.kt +91 -0
  50. package/android/src/main/java/com/renderrichtext/renderer/Renderer.kt +148 -0
  51. package/android/src/main/java/com/renderrichtext/renderer/SoftBreakRenderer.kt +17 -0
  52. package/android/src/main/java/com/renderrichtext/renderer/SpanStyleCache.kt +143 -0
  53. package/android/src/main/java/com/renderrichtext/renderer/StrikethroughRenderer.kt +28 -0
  54. package/android/src/main/java/com/renderrichtext/renderer/StrongRenderer.kt +28 -0
  55. package/android/src/main/java/com/renderrichtext/renderer/TextRenderer.kt +31 -0
  56. package/android/src/main/java/com/renderrichtext/renderer/ThematicBreakRenderer.kt +46 -0
  57. package/android/src/main/java/com/renderrichtext/renderer/UnderlineRenderer.kt +28 -0
  58. package/android/src/main/java/com/renderrichtext/spans/BaseListSpan.kt +164 -0
  59. package/android/src/main/java/com/renderrichtext/spans/BaselineShiftSpan.kt +27 -0
  60. package/android/src/main/java/com/renderrichtext/spans/BlockquoteSpan.kt +302 -0
  61. package/android/src/main/java/com/renderrichtext/spans/CodeBackgroundSpan.kt +205 -0
  62. package/android/src/main/java/com/renderrichtext/spans/CodeBlockSpan.kt +216 -0
  63. package/android/src/main/java/com/renderrichtext/spans/CodeSpan.kt +32 -0
  64. package/android/src/main/java/com/renderrichtext/spans/EmphasisSpan.kt +42 -0
  65. package/android/src/main/java/com/renderrichtext/spans/FactoidBackgroundSpan.kt +265 -0
  66. package/android/src/main/java/com/renderrichtext/spans/FactoidPaddingSpan.kt +258 -0
  67. package/android/src/main/java/com/renderrichtext/spans/FactoidSpan.kt +32 -0
  68. package/android/src/main/java/com/renderrichtext/spans/FadeInSpan.kt +29 -0
  69. package/android/src/main/java/com/renderrichtext/spans/HeadingSpan.kt +38 -0
  70. package/android/src/main/java/com/renderrichtext/spans/HighlightSpan.kt +80 -0
  71. package/android/src/main/java/com/renderrichtext/spans/ImageSpan.kt +481 -0
  72. package/android/src/main/java/com/renderrichtext/spans/InlineBackgroundGeometry.kt +42 -0
  73. package/android/src/main/java/com/renderrichtext/spans/LineHeightSpan.kt +39 -0
  74. package/android/src/main/java/com/renderrichtext/spans/LinkPillIconCache.kt +154 -0
  75. package/android/src/main/java/com/renderrichtext/spans/LinkPillSpan.kt +381 -0
  76. package/android/src/main/java/com/renderrichtext/spans/LinkSpan.kt +68 -0
  77. package/android/src/main/java/com/renderrichtext/spans/ListItemSpacingSpan.kt +33 -0
  78. package/android/src/main/java/com/renderrichtext/spans/ListMarkerAnchorSpan.kt +38 -0
  79. package/android/src/main/java/com/renderrichtext/spans/MarginBottomSpan.kt +76 -0
  80. package/android/src/main/java/com/renderrichtext/spans/MentionChipSpan.kt +132 -0
  81. package/android/src/main/java/com/renderrichtext/spans/MentionSpan.kt +46 -0
  82. package/android/src/main/java/com/renderrichtext/spans/OrderedListSpan.kt +127 -0
  83. package/android/src/main/java/com/renderrichtext/spans/SpoilerSpan.kt +33 -0
  84. package/android/src/main/java/com/renderrichtext/spans/StrikethroughSpan.kt +12 -0
  85. package/android/src/main/java/com/renderrichtext/spans/StrongSpan.kt +39 -0
  86. package/android/src/main/java/com/renderrichtext/spans/TaskListSpan.kt +143 -0
  87. package/android/src/main/java/com/renderrichtext/spans/TextSpan.kt +26 -0
  88. package/android/src/main/java/com/renderrichtext/spans/ThematicBreakSpan.kt +69 -0
  89. package/android/src/main/java/com/renderrichtext/spans/UnorderedListSpan.kt +96 -0
  90. package/android/src/main/java/com/renderrichtext/spoiler/SpoilerStubs.kt +46 -0
  91. package/android/src/main/java/com/renderrichtext/styles/BaseBlockStyle.kt +11 -0
  92. package/android/src/main/java/com/renderrichtext/styles/BlockquoteStyle.kt +83 -0
  93. package/android/src/main/java/com/renderrichtext/styles/CodeBlockStyle.kt +80 -0
  94. package/android/src/main/java/com/renderrichtext/styles/CodeStyle.kt +26 -0
  95. package/android/src/main/java/com/renderrichtext/styles/EmphasisStyle.kt +21 -0
  96. package/android/src/main/java/com/renderrichtext/styles/FactoidStyle.kt +21 -0
  97. package/android/src/main/java/com/renderrichtext/styles/HeadingStyle.kt +33 -0
  98. package/android/src/main/java/com/renderrichtext/styles/HighlightStyle.kt +19 -0
  99. package/android/src/main/java/com/renderrichtext/styles/ImageStyle.kt +29 -0
  100. package/android/src/main/java/com/renderrichtext/styles/InlineImageStyle.kt +17 -0
  101. package/android/src/main/java/com/renderrichtext/styles/InlineMathStyle.kt +17 -0
  102. package/android/src/main/java/com/renderrichtext/styles/LinkPillContent.kt +36 -0
  103. package/android/src/main/java/com/renderrichtext/styles/LinkPillStyle.kt +44 -0
  104. package/android/src/main/java/com/renderrichtext/styles/LinkStyle.kt +23 -0
  105. package/android/src/main/java/com/renderrichtext/styles/LinkVariantEntry.kt +34 -0
  106. package/android/src/main/java/com/renderrichtext/styles/ListStyle.kt +65 -0
  107. package/android/src/main/java/com/renderrichtext/styles/MathStyle.kt +29 -0
  108. package/android/src/main/java/com/renderrichtext/styles/MentionStyle.kt +19 -0
  109. package/android/src/main/java/com/renderrichtext/styles/ParagraphStyle.kt +33 -0
  110. package/android/src/main/java/com/renderrichtext/styles/SpoilerStyle.kt +27 -0
  111. package/android/src/main/java/com/renderrichtext/styles/StrikethroughStyle.kt +17 -0
  112. package/android/src/main/java/com/renderrichtext/styles/StrongStyle.kt +21 -0
  113. package/android/src/main/java/com/renderrichtext/styles/StyleConfig.kt +411 -0
  114. package/android/src/main/java/com/renderrichtext/styles/StyleParser.kt +92 -0
  115. package/android/src/main/java/com/renderrichtext/styles/SubscriptStyle.kt +16 -0
  116. package/android/src/main/java/com/renderrichtext/styles/SuperscriptStyle.kt +16 -0
  117. package/android/src/main/java/com/renderrichtext/styles/TableStyle.kt +75 -0
  118. package/android/src/main/java/com/renderrichtext/styles/TaskListStyle.kt +29 -0
  119. package/android/src/main/java/com/renderrichtext/styles/TextAlignment.kt +32 -0
  120. package/android/src/main/java/com/renderrichtext/styles/ThematicBreakStyle.kt +23 -0
  121. package/android/src/main/java/com/renderrichtext/styles/UnderlineStyle.kt +17 -0
  122. package/android/src/main/java/com/renderrichtext/styles/VideoStyle.kt +31 -0
  123. package/android/src/main/java/com/renderrichtext/utils/common/BorderStyleUtils.kt +86 -0
  124. package/android/src/main/java/com/renderrichtext/utils/common/BreakStrategyUtils.kt +32 -0
  125. package/android/src/main/java/com/renderrichtext/utils/common/CodeBlockHighlighter.kt +36 -0
  126. package/android/src/main/java/com/renderrichtext/utils/common/CodeBlockNode.kt +42 -0
  127. package/android/src/main/java/com/renderrichtext/utils/common/EllipsizeUtils.kt +27 -0
  128. package/android/src/main/java/com/renderrichtext/utils/common/EnrichedTiptapAncestorUtils.kt +10 -0
  129. package/android/src/main/java/com/renderrichtext/utils/common/FeatureFlags.kt +9 -0
  130. package/android/src/main/java/com/renderrichtext/utils/common/LinkRecognitionProps.kt +31 -0
  131. package/android/src/main/java/com/renderrichtext/utils/common/LinkRegexConfig.kt +56 -0
  132. package/android/src/main/java/com/renderrichtext/utils/common/MotionPreferences.kt +18 -0
  133. package/android/src/main/java/com/renderrichtext/utils/common/ReadableMapExtensions.kt +28 -0
  134. package/android/src/main/java/com/renderrichtext/utils/common/TiptapViewManagerUtils.kt +242 -0
  135. package/android/src/main/java/com/renderrichtext/utils/common/layout/RTLUtils.kt +6 -0
  136. package/android/src/main/java/com/renderrichtext/utils/common/serialization/TiptapASTSerializer.kt +368 -0
  137. package/android/src/main/java/com/renderrichtext/utils/text/ImageCache.kt +88 -0
  138. package/android/src/main/java/com/renderrichtext/utils/text/ImageDownloader.kt +203 -0
  139. package/android/src/main/java/com/renderrichtext/utils/text/LocalImageLoader.kt +167 -0
  140. package/android/src/main/java/com/renderrichtext/utils/text/TailFadeInAnimator.kt +84 -0
  141. package/android/src/main/java/com/renderrichtext/utils/text/conversion/HTMLGenerator.kt +1105 -0
  142. package/android/src/main/java/com/renderrichtext/utils/text/conversion/TiptapExtractor.kt +423 -0
  143. package/android/src/main/java/com/renderrichtext/utils/text/extensions/ParagraphUtils.kt +9 -0
  144. package/android/src/main/java/com/renderrichtext/utils/text/extensions/SpannableExtensions.kt +9 -0
  145. package/android/src/main/java/com/renderrichtext/utils/text/extensions/TextPaintExtensions.kt +76 -0
  146. package/android/src/main/java/com/renderrichtext/utils/text/interaction/CheckboxTouchHelper.kt +61 -0
  147. package/android/src/main/java/com/renderrichtext/utils/text/interaction/TaskListTapUtils.kt +286 -0
  148. package/android/src/main/java/com/renderrichtext/utils/text/span/ApplyLineHeight.kt +39 -0
  149. package/android/src/main/java/com/renderrichtext/utils/text/span/BlockSpacing.kt +142 -0
  150. package/android/src/main/java/com/renderrichtext/utils/text/span/SpanFlags.kt +43 -0
  151. package/android/src/main/java/com/renderrichtext/utils/text/span/WidthAwareSpans.kt +30 -0
  152. package/android/src/main/java/com/renderrichtext/utils/text/view/CodeBlockPressHost.kt +15 -0
  153. package/android/src/main/java/com/renderrichtext/utils/text/view/ImagePressHost.kt +19 -0
  154. package/android/src/main/java/com/renderrichtext/utils/text/view/LinkEvents.kt +88 -0
  155. package/android/src/main/java/com/renderrichtext/utils/text/view/LinkLongPressMovementMethod.kt +449 -0
  156. package/android/src/main/java/com/renderrichtext/utils/text/view/SelectionActionMode.kt +235 -0
  157. package/android/src/main/java/com/renderrichtext/utils/text/view/TextSelectionColors.kt +48 -0
  158. package/android/src/main/java/com/renderrichtext/utils/text/view/TextViewSetup.kt +119 -0
  159. package/android/src/main/java/com/renderrichtext/views/ContextMenuPopup.kt +217 -0
  160. package/android/src/main/java/com/renderrichtext/views/CopyGlyph.kt +101 -0
  161. package/android/src/main/java/com/renderrichtext/views/WrapGlyph.kt +40 -0
  162. package/android/src/main/jni/CMakeLists.txt +52 -0
  163. package/android/src/main/jni/react/renderer/components/RichTextSpec/ComponentDescriptors.h +30 -0
  164. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextComponentDescriptor.h +29 -0
  165. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextMeasurementManager.cpp +45 -0
  166. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextMeasurementManager.h +21 -0
  167. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextShadowNode.cpp +38 -0
  168. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextShadowNode.h +53 -0
  169. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextState.cpp +9 -0
  170. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextState.h +24 -0
  171. package/android/src/main/jni/react/renderer/components/RichTextSpec/conversions.h +40 -0
  172. package/cpp/parser/TiptapASTNode.hpp +68 -0
  173. package/cpp/parser/TiptapParser.cpp +233 -0
  174. package/cpp/parser/TiptapParser.hpp +13 -0
  175. package/cpp/vendor/nlohmann/json.hpp +24765 -0
  176. package/ios/EnrichedTiptapText.h +27 -0
  177. package/ios/EnrichedTiptapText.mm +1222 -0
  178. package/ios/attachments/ENRMImageAttachment.h +51 -0
  179. package/ios/attachments/ENRMImageAttachment.m +398 -0
  180. package/ios/attachments/ENRMLinkPillAttachment.h +47 -0
  181. package/ios/attachments/ENRMLinkPillAttachment.m +324 -0
  182. package/ios/attachments/ENRMLinkPillIconCache.h +28 -0
  183. package/ios/attachments/ENRMLinkPillIconCache.m +181 -0
  184. package/ios/attachments/ENRMLinkPillText.h +27 -0
  185. package/ios/attachments/ENRMLinkPillText.m +118 -0
  186. package/ios/attachments/ENRMMathInlineAttachment.h +7 -0
  187. package/ios/attachments/ENRMMathInlineAttachment.m +4 -0
  188. package/ios/attachments/ThematicBreakAttachment.h +15 -0
  189. package/ios/attachments/ThematicBreakAttachment.m +33 -0
  190. package/ios/internals/ENRMViewFreeMeasurement.h +208 -0
  191. package/ios/internals/EnrichedTiptapTextComponentDescriptor.h +19 -0
  192. package/ios/internals/EnrichedTiptapTextShadowNode.h +43 -0
  193. package/ios/internals/EnrichedTiptapTextShadowNode.mm +69 -0
  194. package/ios/internals/EnrichedTiptapTextState.h +24 -0
  195. package/ios/internals/MeasurementCache.h +308 -0
  196. package/ios/internals/ShadowMeasurementUtils.h +145 -0
  197. package/ios/parser/ENRMTextLinkAttributes.h +7 -0
  198. package/ios/parser/ENRMTiptapParser.h +33 -0
  199. package/ios/parser/ENRMTiptapParser.mm +73 -0
  200. package/ios/parser/TiptapASTNode.h +57 -0
  201. package/ios/parser/TiptapASTNode.m +52 -0
  202. package/ios/parser/TiptapParserBridge.mm +179 -0
  203. package/ios/renderer/AttributedRenderer.h +23 -0
  204. package/ios/renderer/AttributedRenderer.m +229 -0
  205. package/ios/renderer/BaseRenderer.h +33 -0
  206. package/ios/renderer/BaseRenderer.m +46 -0
  207. package/ios/renderer/BaselineShiftTextAttributes.h +10 -0
  208. package/ios/renderer/BaselineShiftTextAttributes.m +26 -0
  209. package/ios/renderer/BlankLineRenderer.h +4 -0
  210. package/ios/renderer/BlankLineRenderer.m +51 -0
  211. package/ios/renderer/BlockquoteRenderer.h +6 -0
  212. package/ios/renderer/BlockquoteRenderer.m +258 -0
  213. package/ios/renderer/CodeBlockRenderer.h +9 -0
  214. package/ios/renderer/CodeBlockRenderer.m +99 -0
  215. package/ios/renderer/CodeRenderer.h +9 -0
  216. package/ios/renderer/CodeRenderer.m +69 -0
  217. package/ios/renderer/ENRMBlockquoteTextRenderer.h +25 -0
  218. package/ios/renderer/ENRMBlockquoteTextRenderer.m +48 -0
  219. package/ios/renderer/EmphasisRenderer.h +5 -0
  220. package/ios/renderer/EmphasisRenderer.m +100 -0
  221. package/ios/renderer/FactoidRenderer.h +9 -0
  222. package/ios/renderer/FactoidRenderer.m +130 -0
  223. package/ios/renderer/HeadingRenderer.h +6 -0
  224. package/ios/renderer/HeadingRenderer.m +103 -0
  225. package/ios/renderer/HighlightRenderer.h +7 -0
  226. package/ios/renderer/HighlightRenderer.m +33 -0
  227. package/ios/renderer/LinkRenderer.h +6 -0
  228. package/ios/renderer/LinkRenderer.m +158 -0
  229. package/ios/renderer/ListItemRenderer.h +24 -0
  230. package/ios/renderer/ListItemRenderer.m +283 -0
  231. package/ios/renderer/ListRenderer.h +13 -0
  232. package/ios/renderer/ListRenderer.m +126 -0
  233. package/ios/renderer/MentionRenderer.h +9 -0
  234. package/ios/renderer/MentionRenderer.m +96 -0
  235. package/ios/renderer/NodeRenderer.h +8 -0
  236. package/ios/renderer/ParagraphRenderer.h +6 -0
  237. package/ios/renderer/ParagraphRenderer.m +85 -0
  238. package/ios/renderer/RenderContext.h +145 -0
  239. package/ios/renderer/RenderContext.m +403 -0
  240. package/ios/renderer/RendererFactory.h +12 -0
  241. package/ios/renderer/RendererFactory.m +158 -0
  242. package/ios/renderer/StrikethroughRenderer.h +5 -0
  243. package/ios/renderer/StrikethroughRenderer.m +30 -0
  244. package/ios/renderer/StrongRenderer.h +5 -0
  245. package/ios/renderer/StrongRenderer.m +88 -0
  246. package/ios/renderer/TextRenderer.h +6 -0
  247. package/ios/renderer/TextRenderer.m +18 -0
  248. package/ios/renderer/ThematicBreakRenderer.h +4 -0
  249. package/ios/renderer/ThematicBreakRenderer.m +42 -0
  250. package/ios/renderer/UnderlineRenderer.h +4 -0
  251. package/ios/renderer/UnderlineRenderer.m +41 -0
  252. package/ios/styles/ENRMFontSlot.h +12 -0
  253. package/ios/styles/ENRMFontSlot.m +24 -0
  254. package/ios/styles/StyleConfig.h +488 -0
  255. package/ios/styles/StyleConfig.mm +2820 -0
  256. package/ios/utils/AccessibilityInfo.h +35 -0
  257. package/ios/utils/AccessibilityInfo.m +24 -0
  258. package/ios/utils/BlockquoteBorder.h +26 -0
  259. package/ios/utils/BlockquoteBorder.m +294 -0
  260. package/ios/utils/CodeBackground.h +19 -0
  261. package/ios/utils/CodeBackground.m +197 -0
  262. package/ios/utils/CodeBlockBackground.h +17 -0
  263. package/ios/utils/CodeBlockBackground.m +103 -0
  264. package/ios/utils/ContextMenuUtils.h +76 -0
  265. package/ios/utils/ContextMenuUtils.mm +75 -0
  266. package/ios/utils/ENRMAccessibilityLabels.h +40 -0
  267. package/ios/utils/ENRMAccessibilityLabels.m +5 -0
  268. package/ios/utils/ENRMAsyncRenderCoordinator.h +24 -0
  269. package/ios/utils/ENRMAsyncRenderCoordinator.m +37 -0
  270. package/ios/utils/ENRMAtomicSize.h +65 -0
  271. package/ios/utils/ENRMCodeBlockContent.h +37 -0
  272. package/ios/utils/ENRMCodeBlockContent.mm +89 -0
  273. package/ios/utils/ENRMFeatureFlags.h +25 -0
  274. package/ios/utils/ENRMImageDownloader.h +30 -0
  275. package/ios/utils/ENRMImageDownloader.m +168 -0
  276. package/ios/utils/ENRMLatexErrorCoordinator.h +27 -0
  277. package/ios/utils/ENRMLatexErrorCoordinator.mm +75 -0
  278. package/ios/utils/ENRMLatexErrorReporting.h +13 -0
  279. package/ios/utils/ENRMLinkContextMenus.h +93 -0
  280. package/ios/utils/ENRMLinkContextMenus.m +178 -0
  281. package/ios/utils/ENRMLinkRegexConfig.h +30 -0
  282. package/ios/utils/ENRMLinkRegexConfig.mm +104 -0
  283. package/ios/utils/ENRMLinkRegexProps.h +23 -0
  284. package/ios/utils/ENRMLocalImageLoader.h +35 -0
  285. package/ios/utils/ENRMLocalImageLoader.m +111 -0
  286. package/ios/utils/ENRMMenuAction.h +27 -0
  287. package/ios/utils/ENRMMenuAction.m +33 -0
  288. package/ios/utils/ENRMSpoilerOverlayManager.h +15 -0
  289. package/ios/utils/ENRMSpoilerOverlayManager.m +19 -0
  290. package/ios/utils/ENRMSpoilerTapUtils.h +6 -0
  291. package/ios/utils/ENRMSpoilerTapUtils.m +11 -0
  292. package/ios/utils/ENRMTailFadeInAnimator.h +16 -0
  293. package/ios/utils/ENRMTailFadeInAnimator.m +161 -0
  294. package/ios/utils/ENRMTextHitTest.h +23 -0
  295. package/ios/utils/ENRMTextHitTest.m +20 -0
  296. package/ios/utils/ENRMTextInteractionUtils.h +20 -0
  297. package/ios/utils/ENRMTextInteractionUtils.m +15 -0
  298. package/ios/utils/ENRMTextRenderer.h +38 -0
  299. package/ios/utils/ENRMTextRenderer.m +54 -0
  300. package/ios/utils/ENRMTextViewSetup.h +132 -0
  301. package/ios/utils/ENRMUIKit.h +429 -0
  302. package/ios/utils/EditMenuUtils.h +105 -0
  303. package/ios/utils/EditMenuUtils.m +143 -0
  304. package/ios/utils/FactoidBackground.h +20 -0
  305. package/ios/utils/FactoidBackground.m +161 -0
  306. package/ios/utils/FontScaleObserver.h +10 -0
  307. package/ios/utils/FontScaleObserver.m +55 -0
  308. package/ios/utils/FontUtils.h +31 -0
  309. package/ios/utils/FontUtils.m +63 -0
  310. package/ios/utils/HTMLGenerator.h +25 -0
  311. package/ios/utils/HTMLGenerator.m +1041 -0
  312. package/ios/utils/HeightUpdateUtils.h +25 -0
  313. package/ios/utils/ImageRequestHeaderUtils.h +35 -0
  314. package/ios/utils/LastElementUtils.h +97 -0
  315. package/ios/utils/LinkContextMenuUtils.h +55 -0
  316. package/ios/utils/LinkPillContentUtils.h +46 -0
  317. package/ios/utils/LinkTapUtils.h +56 -0
  318. package/ios/utils/LinkTapUtils.m +139 -0
  319. package/ios/utils/ListMarkerDrawer.h +15 -0
  320. package/ios/utils/ListMarkerDrawer.m +239 -0
  321. package/ios/utils/MentionBackground.h +26 -0
  322. package/ios/utils/MentionBackground.m +86 -0
  323. package/ios/utils/ParagraphStyleUtils.h +87 -0
  324. package/ios/utils/ParagraphStyleUtils.m +484 -0
  325. package/ios/utils/PasteboardUtils.h +48 -0
  326. package/ios/utils/PasteboardUtils.m +166 -0
  327. package/ios/utils/RTFExportUtils.h +24 -0
  328. package/ios/utils/RTFExportUtils.m +299 -0
  329. package/ios/utils/RuntimeKeys.h +44 -0
  330. package/ios/utils/RuntimeKeys.m +13 -0
  331. package/ios/utils/SelectionColorUtils.h +14 -0
  332. package/ios/utils/StylePropsUtils.h +1340 -0
  333. package/ios/utils/TaskListTapUtils.h +6 -0
  334. package/ios/utils/TaskListTapUtils.m +14 -0
  335. package/ios/utils/TextViewLayoutManager.h +14 -0
  336. package/ios/utils/TextViewLayoutManager.mm +198 -0
  337. package/ios/utils/TiptapAccessibilityElementBuilder.h +43 -0
  338. package/ios/utils/TiptapAccessibilityElementBuilder.m +432 -0
  339. package/ios/utils/TiptapExtractor.h +17 -0
  340. package/ios/utils/TiptapExtractor.m +367 -0
  341. package/ios/views/ENRMTiptapTextView.h +25 -0
  342. package/ios/views/ENRMTiptapTextView.m +247 -0
  343. package/package.json +70 -6
  344. package/react-native.config.js +10 -0
  345. package/src/EnrichedTiptapTextNativeComponent.ts +739 -0
  346. package/src/RichText.tsx +164 -0
  347. package/src/admonitionDefaults.ts +58 -0
  348. package/src/darkModeTextStyle.ts +99 -0
  349. package/src/headingDefaults.ts +21 -0
  350. package/src/index.tsx +4 -0
  351. package/src/linkVariantUtils.ts +120 -0
  352. package/src/normalizeTiptapStyle.ts +397 -0
  353. package/src/rn-globals.d.ts +1 -0
  354. package/src/styleUtils.ts +107 -0
  355. package/src/types/TiptapStyle.ts +532 -0
  356. package/src/types/TiptapStyleInternal.ts +299 -0
  357. package/src/types/linkPill.ts +9 -0
  358. package/src/utils/isDev.ts +4 -0
@@ -0,0 +1,481 @@
1
+ package com.renderrichtext.spans
2
+
3
+ import android.content.Context
4
+ import android.graphics.Bitmap
5
+ import android.graphics.Canvas
6
+ import android.graphics.Color
7
+ import android.graphics.Paint
8
+ import android.graphics.Path
9
+ import android.graphics.drawable.Drawable
10
+ import android.net.Uri
11
+ import android.os.Build
12
+ import android.text.Spannable
13
+ import android.text.Spanned
14
+ import android.text.style.LeadingMarginSpan
15
+ import android.util.Log
16
+ import android.widget.TextView
17
+ import androidx.core.graphics.createBitmap
18
+ import androidx.core.graphics.drawable.toDrawable
19
+ import androidx.core.graphics.withClip
20
+ import androidx.core.graphics.withSave
21
+ import com.renderrichtext.EnrichedTiptapText
22
+ import com.renderrichtext.styles.StyleConfig
23
+ import com.renderrichtext.utils.common.findEnrichedTiptapTextAncestor
24
+ import com.renderrichtext.utils.text.ImageCache
25
+ import com.renderrichtext.utils.text.ImageDownloader
26
+ import com.renderrichtext.utils.text.LocalImageLoader
27
+ import java.lang.ref.WeakReference
28
+ import java.util.concurrent.Executors
29
+ import android.text.style.ImageSpan as AndroidImageSpan
30
+ import android.text.style.LineHeightSpan as AndroidLineHeightSpan
31
+
32
+ class ImageSpan(
33
+ private val context: Context,
34
+ val imageUrl: String,
35
+ styleConfig: StyleConfig,
36
+ val isInline: Boolean = false,
37
+ val altText: String = "",
38
+ ) : AndroidImageSpan(
39
+ Color.TRANSPARENT.toDrawable(),
40
+ imageUrl,
41
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) ALIGN_CENTER else ALIGN_BASELINE,
42
+ ),
43
+ AndroidLineHeightSpan {
44
+ private var loadedDrawable: Drawable? = null
45
+ private val height: Int = if (isInline) styleConfig.inlineImageStyle.size.toInt() else styleConfig.imageStyle.height.toInt()
46
+
47
+ private val borderRadiusPx: Int = styleConfig.imageStyle.borderRadius.toInt()
48
+ private val maxHeightPx: Int = if (isInline) 0 else styleConfig.imageStyle.maxHeight.toInt()
49
+ private val aspectRatio: Float = if (isInline) 0f else styleConfig.imageStyle.aspectRatio
50
+ private val resizeMode: String = if (isInline) "" else styleConfig.imageStyle.resizeMode
51
+
52
+ // so legacy always implies a fixed height box.
53
+ private val legacySizing: Boolean = resizeMode.isEmpty()
54
+
55
+ private val dynamicBoxHeight: Boolean = !isInline && (maxHeightPx > 0 || aspectRatio > 0f)
56
+
57
+ private var boxHeight: Int =
58
+ when {
59
+ isInline -> height
60
+ maxHeightPx > 0 -> maxHeightPx
61
+ else -> height
62
+ }
63
+
64
+ private val requestHeaders: Map<String, String> = styleConfig.imageRequestHeaders
65
+ private val requestKey: String = ImageCache.requestKey(imageUrl, requestHeaders)
66
+
67
+ private var cachedWidth: Int = 0
68
+ private var viewRef: WeakReference<TextView>? = null
69
+ private var sourceDrawable: Drawable? = null
70
+ private var onBoxHeightChanged: (() -> Unit)? = null
71
+
72
+ private fun intrinsicImageSize(): Pair<Int, Int> {
73
+ sourceDrawable?.let { return it.intrinsicWidth to it.intrinsicHeight }
74
+ val cached = ImageCache.getOriginal(imageUrl) ?: return 0 to 0
75
+ return cached.width to cached.height
76
+ }
77
+
78
+ // Sizing precedence: aspectRatio > maxHeight > height. Returns the legacy fixed
79
+ // height when no new knob is set.
80
+ private fun resolveBoxHeight(targetWidth: Int): Int {
81
+ if (isInline) return height
82
+ if (aspectRatio > 0f && targetWidth > 0) {
83
+ return (targetWidth / aspectRatio).toInt().coerceAtLeast(1)
84
+ }
85
+ if (maxHeightPx > 0) {
86
+ val (intrinsicWidth, intrinsicHeight) = intrinsicImageSize()
87
+ if (targetWidth > 0 && intrinsicWidth > 0 && intrinsicHeight > 0) {
88
+ val fitted = targetWidth.toFloat() * intrinsicHeight / intrinsicWidth
89
+ return minOf(maxHeightPx.toFloat(), fitted).toInt().coerceAtLeast(1)
90
+ }
91
+ return maxHeightPx
92
+ }
93
+ return height
94
+ }
95
+
96
+ fun prepareForMeasurement(
97
+ text: Spanned,
98
+ widthPx: Int,
99
+ ) {
100
+ if (!dynamicBoxHeight || widthPx <= 0) return
101
+ val available = availableWidthIn(text, widthPx)
102
+ if (available > 0) boxHeight = resolveBoxHeight(available)
103
+ }
104
+
105
+ init {
106
+ loadImage()
107
+ }
108
+
109
+ private fun loadImage() {
110
+ val scheme = Uri.parse(imageUrl).scheme?.lowercase()
111
+ if (scheme == "http" || scheme == "https") {
112
+ ImageDownloader.download(context, imageUrl, requestHeaders) { bitmap ->
113
+ if (bitmap != null) {
114
+ sourceDrawable = bitmap.toDrawable(context.resources)
115
+ wrapAndAssignDrawable()
116
+ }
117
+ }
118
+ } else {
119
+ val cached = ImageCache.getOriginal(imageUrl)
120
+ val bitmap = cached ?: LocalImageLoader.load(context, imageUrl)
121
+ if (bitmap != null) {
122
+ if (cached == null) ImageCache.putOriginal(imageUrl, bitmap)
123
+ sourceDrawable = bitmap.toDrawable(context.resources)
124
+ wrapAndAssignDrawable()
125
+ }
126
+ }
127
+ }
128
+
129
+ private fun wrapAndAssignDrawable() {
130
+ val base = sourceDrawable ?: return
131
+ val targetWidth =
132
+ if (isInline) {
133
+ height
134
+ } else {
135
+ val available = viewRef?.get()?.let { getAvailableWidth(it) } ?: cachedWidth
136
+ available.coerceAtLeast(0)
137
+ }
138
+
139
+ boxHeight = resolveBoxHeight(targetWidth)
140
+
141
+ val cachedBitmap = ImageCache.getProcessed(requestKey, targetWidth, boxHeight, borderRadiusPx, resizeMode)
142
+
143
+ if (cachedBitmap != null) {
144
+ loadedDrawable =
145
+ cachedBitmap.toDrawable(context.resources).apply {
146
+ setBounds(0, 0, targetWidth, boxHeight)
147
+ }
148
+ } else {
149
+ loadedDrawable =
150
+ ScaledImageDrawable(
151
+ imageDrawable = base,
152
+ targetWidth = targetWidth,
153
+ targetHeight = boxHeight,
154
+ borderRadius = borderRadiusPx,
155
+ isBlockImage = !isInline,
156
+ resizeMode = resizeMode,
157
+ legacySizing = legacySizing,
158
+ cacheKey = CacheKey(requestKey, targetWidth, boxHeight, borderRadiusPx, resizeMode),
159
+ )
160
+ }
161
+ requestReflow()
162
+ }
163
+
164
+ private fun requestReflow() {
165
+ val view = viewRef?.get() ?: return
166
+ val text = view.text
167
+ if (text is Spannable) {
168
+ val start = text.getSpanStart(this)
169
+ val end = text.getSpanEnd(this)
170
+ if (start != -1 && end != -1) {
171
+ text.setSpan(this, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
172
+ }
173
+ } else {
174
+ view.invalidate()
175
+ view.requestLayout()
176
+ }
177
+ notifyBoxHeightMayHaveChanged(view)
178
+ }
179
+
180
+ // With maxHeight/aspectRatio the box height can change after the image loads
181
+ // (maxHeight fitted, aspectRatio at a late width). The React container height
182
+ // was measured with the pre-load value, so ask the owning component to
183
+ // re-measure; it only propagates when the stored height actually changed.
184
+ private fun notifyBoxHeightMayHaveChanged(view: TextView) {
185
+ if (!dynamicBoxHeight) return
186
+ // A self-measuring host (e.g. a table cell) sizes the image box itself and drives
187
+ // its own re-layout, so bubbling up to the component would churn it and orphan this
188
+ // freshly registered view. Let the host re-measure locally instead.
189
+ onBoxHeightChanged?.let {
190
+ it()
191
+ return
192
+ }
193
+ if (view is EnrichedTiptapText) {
194
+ view.layoutManager.invalidateLayout()
195
+ return
196
+ }
197
+ view.findEnrichedTiptapTextAncestor()?.layoutManager?.invalidateLayout()
198
+ }
199
+
200
+ fun registerTextView(
201
+ view: TextView,
202
+ onBoxHeightChanged: (() -> Unit)? = null,
203
+ ) {
204
+ this.onBoxHeightChanged = onBoxHeightChanged
205
+ viewRef = WeakReference(view)
206
+ if (!isInline) {
207
+ val availableWidth = getAvailableWidth(view)
208
+ if (availableWidth > 0) {
209
+ updateWidthAndRecreate(availableWidth)
210
+ }
211
+ view.post {
212
+ val postWidth = getAvailableWidth(view)
213
+ if (postWidth != cachedWidth) updateWidthAndRecreate(postWidth)
214
+ }
215
+ }
216
+ }
217
+
218
+ private fun updateWidthAndRecreate(newWidth: Int) {
219
+ if (newWidth <= 0 || cachedWidth == newWidth) return
220
+ cachedWidth = newWidth
221
+ if (sourceDrawable != null) {
222
+ wrapAndAssignDrawable()
223
+ return
224
+ }
225
+ if (dynamicBoxHeight) {
226
+ val newBoxHeight = resolveBoxHeight(newWidth)
227
+ if (newBoxHeight != boxHeight) {
228
+ boxHeight = newBoxHeight
229
+ requestReflow()
230
+ }
231
+ }
232
+ }
233
+
234
+ private fun getAvailableWidth(view: TextView): Int {
235
+ val baseWidth = (view.layout?.width ?: view.width).coerceAtLeast(0)
236
+ val text = view.text as? Spanned ?: return baseWidth
237
+ return availableWidthIn(text, baseWidth)
238
+ }
239
+
240
+ private fun availableWidthIn(
241
+ text: Spanned,
242
+ baseWidth: Int,
243
+ ): Int {
244
+ val start = text.getSpanStart(this).takeIf { it >= 0 } ?: return baseWidth
245
+ val end = text.getSpanEnd(this).coerceAtLeast(start)
246
+ // Subtract leading margins (list bullet/indent, blockquote bar) that consume
247
+ // horizontal space on the image's line. first=true is correct because a block
248
+ // image sits on its own line — the "first line" from the layout's perspective.
249
+ val totalMargin =
250
+ text
251
+ .getSpans(start, end, LeadingMarginSpan::class.java)
252
+ .sumOf { it.getLeadingMargin(true) }
253
+ return (baseWidth - totalMargin).coerceAtLeast(0)
254
+ }
255
+
256
+ override fun getDrawable(): Drawable {
257
+ val drawable = loadedDrawable ?: transparentDrawable
258
+ if (drawable !is ScaledImageDrawable) {
259
+ val drawableWidth = if (isInline) height else cachedWidth.takeIf { it > 0 } ?: drawable.intrinsicWidth
260
+ drawable.setBounds(0, 0, drawableWidth.coerceAtLeast(0), boxHeight.coerceAtLeast(0))
261
+ }
262
+ return drawable
263
+ }
264
+
265
+ override fun getSize(
266
+ paint: Paint,
267
+ text: CharSequence?,
268
+ start: Int,
269
+ end: Int,
270
+ fm: Paint.FontMetricsInt?,
271
+ ): Int = getDrawable().bounds.right
272
+
273
+ override fun chooseHeight(
274
+ text: CharSequence?,
275
+ start: Int,
276
+ end: Int,
277
+ spanstartv: Int,
278
+ lineHeight: Int,
279
+ fm: Paint.FontMetricsInt?,
280
+ ) {
281
+ if (fm == null || isInline) return
282
+ val currentLineHeight = fm.descent - fm.ascent
283
+ if (boxHeight > currentLineHeight) {
284
+ val extraHeight = boxHeight - currentLineHeight
285
+ fm.descent += extraHeight
286
+ fm.bottom += extraHeight
287
+ }
288
+ }
289
+
290
+ override fun draw(
291
+ canvas: Canvas,
292
+ text: CharSequence?,
293
+ start: Int,
294
+ end: Int,
295
+ x: Float,
296
+ top: Int,
297
+ y: Int,
298
+ bottom: Int,
299
+ paint: Paint,
300
+ ) {
301
+ val drawable = getDrawable()
302
+ canvas.withSave {
303
+ if (isInline) {
304
+ val imageHeight = drawable.bounds.height()
305
+ translate(x, (y - imageHeight + (imageHeight * 0.1f)))
306
+ } else {
307
+ translate(x, top.toFloat())
308
+ }
309
+ drawable.draw(this)
310
+ }
311
+ }
312
+
313
+ private data class CacheKey(
314
+ val url: String,
315
+ val width: Int,
316
+ val height: Int,
317
+ val borderRadius: Int,
318
+ val resizeMode: String,
319
+ )
320
+
321
+ private class ScaledImageDrawable(
322
+ private val imageDrawable: Drawable,
323
+ private val targetWidth: Int,
324
+ private val targetHeight: Int,
325
+ private val borderRadius: Int,
326
+ isBlockImage: Boolean,
327
+ private val resizeMode: String = "",
328
+ private val legacySizing: Boolean = true,
329
+ private val cacheKey: CacheKey? = null,
330
+ ) : Drawable() {
331
+ private val clipPath: Path?
332
+ private var hasCached = false
333
+
334
+ init {
335
+ setBounds(0, 0, targetWidth, targetHeight)
336
+ val intrinsicWidth = imageDrawable.intrinsicWidth
337
+ val intrinsicHeight = imageDrawable.intrinsicHeight
338
+
339
+ // New sizing needs a clip to the box so cropping
340
+ // modes don't spill over neighbouring lines.
341
+ val clipToBox = isBlockImage && !legacySizing
342
+
343
+ val (scaledWidth, scaledHeight) =
344
+ if (intrinsicWidth > 0 && intrinsicHeight > 0) {
345
+ when {
346
+ !isBlockImage -> {
347
+ val scale = minOf(targetWidth.toFloat() / intrinsicWidth, targetHeight.toFloat() / intrinsicHeight)
348
+ (intrinsicWidth * scale).toInt() to (intrinsicHeight * scale).toInt()
349
+ }
350
+
351
+ legacySizing -> {
352
+ val scale = targetWidth.toFloat() / intrinsicWidth
353
+ targetWidth to (intrinsicHeight * scale).toInt()
354
+ }
355
+
356
+ else -> {
357
+ resizeModeSize(intrinsicWidth, intrinsicHeight)
358
+ }
359
+ }
360
+ } else {
361
+ targetWidth to targetHeight
362
+ }
363
+
364
+ val left = (targetWidth - scaledWidth) / 2
365
+ val top = (targetHeight - scaledHeight) / 2
366
+ imageDrawable.setBounds(left, top, left + scaledWidth, top + scaledHeight)
367
+
368
+ val clipLeft = maxOf(0, left).toFloat()
369
+ val clipTop = maxOf(0, top).toFloat()
370
+ val clipRight = minOf(targetWidth, left + scaledWidth).toFloat()
371
+ val clipBottom = minOf(targetHeight, top + scaledHeight).toFloat()
372
+ clipPath =
373
+ when {
374
+ borderRadius > 0 -> {
375
+ Path().apply {
376
+ addRoundRect(
377
+ clipLeft,
378
+ clipTop,
379
+ clipRight,
380
+ clipBottom,
381
+ borderRadius.toFloat(),
382
+ borderRadius.toFloat(),
383
+ Path.Direction.CW,
384
+ )
385
+ }
386
+ }
387
+
388
+ clipToBox -> {
389
+ Path().apply {
390
+ addRect(clipLeft, clipTop, clipRight, clipBottom, Path.Direction.CW)
391
+ }
392
+ }
393
+
394
+ else -> {
395
+ null
396
+ }
397
+ }
398
+ }
399
+
400
+ private fun resizeModeSize(
401
+ intrinsicWidth: Int,
402
+ intrinsicHeight: Int,
403
+ ): Pair<Int, Int> {
404
+ if (resizeMode == "stretch") return targetWidth to targetHeight
405
+ val widthScale = targetWidth.toFloat() / intrinsicWidth
406
+ val heightScale = targetHeight.toFloat() / intrinsicHeight
407
+ val scale =
408
+ when (resizeMode) {
409
+ "contain" -> minOf(widthScale, heightScale)
410
+ "center" -> minOf(1f, minOf(widthScale, heightScale))
411
+ "none" -> 1f
412
+ else -> maxOf(widthScale, heightScale) // cover (default)
413
+ }
414
+ return (intrinsicWidth * scale).toInt() to (intrinsicHeight * scale).toInt()
415
+ }
416
+
417
+ override fun draw(canvas: Canvas) {
418
+ if (clipPath != null) {
419
+ canvas.withSave {
420
+ clipPath(clipPath)
421
+ imageDrawable.draw(canvas)
422
+ }
423
+ } else {
424
+ imageDrawable.draw(canvas)
425
+ }
426
+ scheduleCacheBitmap()
427
+ }
428
+
429
+ private fun scheduleCacheBitmap() {
430
+ if (hasCached || cacheKey == null || targetWidth <= 0 || targetHeight <= 0) return
431
+ hasCached = true
432
+ val cachedKey = cacheKey
433
+ val width = targetWidth
434
+ val height = targetHeight
435
+ val path = clipPath
436
+ val source = imageDrawable
437
+ cacheExecutor.execute {
438
+ try {
439
+ val bitmap = createBitmap(width, height)
440
+ val offscreen = Canvas(bitmap)
441
+ if (path != null) {
442
+ offscreen.withClip(path) { source.draw(this) }
443
+ } else {
444
+ source.draw(offscreen)
445
+ }
446
+ ImageCache.putProcessed(
447
+ cachedKey.url,
448
+ cachedKey.width,
449
+ cachedKey.height,
450
+ cachedKey.borderRadius,
451
+ cachedKey.resizeMode,
452
+ bitmap,
453
+ )
454
+ } catch (_: OutOfMemoryError) {
455
+ Log.e("ScaledImageDrawable", "OOM caching bitmap ${width}x$height")
456
+ }
457
+ }
458
+ }
459
+
460
+ override fun setAlpha(alpha: Int) {
461
+ imageDrawable.alpha = alpha
462
+ }
463
+
464
+ override fun setColorFilter(colorFilter: android.graphics.ColorFilter?) {
465
+ imageDrawable.colorFilter = colorFilter
466
+ }
467
+
468
+ @Suppress("DEPRECATION")
469
+ @Deprecated("Deprecated in Java")
470
+ override fun getOpacity(): Int = imageDrawable.opacity
471
+
472
+ override fun getIntrinsicWidth(): Int = targetWidth
473
+
474
+ override fun getIntrinsicHeight(): Int = targetHeight
475
+ }
476
+
477
+ companion object {
478
+ private val transparentDrawable by lazy { Color.TRANSPARENT.toDrawable() }
479
+ private val cacheExecutor = Executors.newSingleThreadExecutor()
480
+ }
481
+ }
@@ -0,0 +1,42 @@
1
+ package com.renderrichtext.spans
2
+
3
+ import android.graphics.Paint
4
+ import android.text.Spanned
5
+ import android.text.StaticLayout
6
+ import android.text.TextPaint
7
+ import android.text.style.LeadingMarginSpan
8
+
9
+ internal object InlineBackgroundGeometry {
10
+ /**
11
+ * Returns the x position of [index] relative to the line's left edge. The measuring layout keeps
12
+ * the line's spans, so a LeadingMarginSpan is already folded into getPrimaryHorizontal — only the
13
+ * early return, which builds no layout, adds [leadingMargin] itself.
14
+ */
15
+ fun horizontalOffset(
16
+ text: CharSequence,
17
+ lineStart: Int,
18
+ lineEnd: Int,
19
+ index: Int,
20
+ paint: Paint,
21
+ leadingMargin: Int,
22
+ ): Float {
23
+ if (index <= lineStart) return leadingMargin.toFloat()
24
+ val lineText = text.subSequence(lineStart, lineEnd)
25
+ val textPaint = paint as? TextPaint ?: TextPaint(paint)
26
+ val layout = StaticLayout.Builder.obtain(lineText, 0, lineText.length, textPaint, 10000).build()
27
+ return layout.getPrimaryHorizontal(index - lineStart)
28
+ }
29
+
30
+ fun leadingMarginAt(
31
+ text: Spanned,
32
+ lineStart: Int,
33
+ ): Int {
34
+ if (lineStart >= text.length) return 0
35
+ val spans = text.getSpans(lineStart, lineStart + 1, LeadingMarginSpan::class.java)
36
+ var margin = 0
37
+ for (span in spans) {
38
+ margin += span.getLeadingMargin(false)
39
+ }
40
+ return margin
41
+ }
42
+ }
@@ -0,0 +1,39 @@
1
+ package com.renderrichtext.spans
2
+
3
+ import android.graphics.Paint
4
+ import kotlin.math.ceil
5
+ import kotlin.math.floor
6
+ import android.text.style.LineHeightSpan as AndroidLineHeightSpan
7
+
8
+ class LineHeightSpan(
9
+ height: Float,
10
+ ) : AndroidLineHeightSpan {
11
+ private val lineHeight: Int = ceil(height.toDouble()).toInt()
12
+
13
+ /**
14
+ * Distributes positive leading evenly above and below the line to reach
15
+ * lineHeight.
16
+ *
17
+ * When the line's natural content is already at least as tall as lineHeight
18
+ * (leading <= 0), we keep the natural metrics and return early. Forcing the
19
+ * smaller lineHeight by shrinking ascent/descent clips tall glyphs (e.g.
20
+ * stacked combining marks) and drags the whole line up (issue #643); letting
21
+ * the line grow to fit its content avoids that. Normally-sized lines have
22
+ * positive leading and are unaffected.
23
+ */
24
+ override fun chooseHeight(
25
+ text: CharSequence?,
26
+ start: Int,
27
+ end: Int,
28
+ spanstartv: Int,
29
+ v: Int,
30
+ fm: Paint.FontMetricsInt?,
31
+ ) {
32
+ if (fm == null) return
33
+
34
+ val leading = lineHeight - ((-fm.ascent) + fm.descent)
35
+ if (leading <= 0) return
36
+ fm.ascent -= ceil(leading / 2.0f).toInt()
37
+ fm.descent += floor(leading / 2.0f).toInt()
38
+ }
39
+ }