@apollohg/react-native-rich-text-editor 2.0.0

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 (494) hide show
  1. package/LICENSE +160 -0
  2. package/README.md +259 -0
  3. package/RUST-STANDARD-LIBRARY-NOTICES.html +8266 -0
  4. package/ReactNativeProseEditor.podspec +59 -0
  5. package/THIRD_PARTY_NOTICES.md +31155 -0
  6. package/android/build.gradle +188 -0
  7. package/android/consumer-rules.pro +18 -0
  8. package/android/expo/build.gradle +17 -0
  9. package/android/src/debug/java/com/apollohg/editor/viewer/PreparedProseDrawInstrumentation.kt +7 -0
  10. package/android/src/main/assets/editor-icons/MaterialIcons.json +2236 -0
  11. package/android/src/main/assets/editor-icons/MaterialIcons.ttf +0 -0
  12. package/android/src/main/java/com/apollohg/editor/AndroidApiCompat.kt +19 -0
  13. package/android/src/main/java/com/apollohg/editor/AndroidCollaborationTransport.kt +860 -0
  14. package/android/src/main/java/com/apollohg/editor/CaretGeometry.kt +91 -0
  15. package/android/src/main/java/com/apollohg/editor/CodeHighlighting.kt +141 -0
  16. package/android/src/main/java/com/apollohg/editor/DecodedBitmapBudget.kt +250 -0
  17. package/android/src/main/java/com/apollohg/editor/EditorAddons.kt +133 -0
  18. package/android/src/main/java/com/apollohg/editor/EditorBlockSpacing.kt +51 -0
  19. package/android/src/main/java/com/apollohg/editor/EditorBoxDrawable.kt +16 -0
  20. package/android/src/main/java/com/apollohg/editor/EditorBoxDrawing.kt +234 -0
  21. package/android/src/main/java/com/apollohg/editor/EditorCheckboxSpan.kt +87 -0
  22. package/android/src/main/java/com/apollohg/editor/EditorCodeHighlighting.kt +120 -0
  23. package/android/src/main/java/com/apollohg/editor/EditorDecorationLayer.java +18 -0
  24. package/android/src/main/java/com/apollohg/editor/EditorDocumentLayout.kt +626 -0
  25. package/android/src/main/java/com/apollohg/editor/EditorEditText.kt +1042 -0
  26. package/android/src/main/java/com/apollohg/editor/EditorEditTextAnnotations.kt +191 -0
  27. package/android/src/main/java/com/apollohg/editor/EditorEditTextCaret.kt +100 -0
  28. package/android/src/main/java/com/apollohg/editor/EditorEditTextClipboard.kt +101 -0
  29. package/android/src/main/java/com/apollohg/editor/EditorEditTextComposition.kt +220 -0
  30. package/android/src/main/java/com/apollohg/editor/EditorEditTextConfiguration.kt +316 -0
  31. package/android/src/main/java/com/apollohg/editor/EditorEditTextCorrection.kt +303 -0
  32. package/android/src/main/java/com/apollohg/editor/EditorEditTextDeferredUpdates.kt +327 -0
  33. package/android/src/main/java/com/apollohg/editor/EditorEditTextDeletion.kt +258 -0
  34. package/android/src/main/java/com/apollohg/editor/EditorEditTextDiagnostics.kt +83 -0
  35. package/android/src/main/java/com/apollohg/editor/EditorEditTextDragDrop.kt +67 -0
  36. package/android/src/main/java/com/apollohg/editor/EditorEditTextExternalComposition.kt +357 -0
  37. package/android/src/main/java/com/apollohg/editor/EditorEditTextHardwareInput.kt +277 -0
  38. package/android/src/main/java/com/apollohg/editor/EditorEditTextImages.kt +280 -0
  39. package/android/src/main/java/com/apollohg/editor/EditorEditTextInputLifecycle.kt +183 -0
  40. package/android/src/main/java/com/apollohg/editor/EditorEditTextInputPreparation.kt +79 -0
  41. package/android/src/main/java/com/apollohg/editor/EditorEditTextInputTraits.kt +384 -0
  42. package/android/src/main/java/com/apollohg/editor/EditorEditTextLayout.kt +100 -0
  43. package/android/src/main/java/com/apollohg/editor/EditorEditTextNativeMutation.kt +243 -0
  44. package/android/src/main/java/com/apollohg/editor/EditorEditTextPlaceholder.kt +154 -0
  45. package/android/src/main/java/com/apollohg/editor/EditorEditTextRenderPatch.kt +347 -0
  46. package/android/src/main/java/com/apollohg/editor/EditorEditTextRendering.kt +313 -0
  47. package/android/src/main/java/com/apollohg/editor/EditorEditTextRustCommands.kt +281 -0
  48. package/android/src/main/java/com/apollohg/editor/EditorEditTextSelection.kt +352 -0
  49. package/android/src/main/java/com/apollohg/editor/EditorEditTextState.kt +113 -0
  50. package/android/src/main/java/com/apollohg/editor/EditorEditTextTaskMarkers.kt +92 -0
  51. package/android/src/main/java/com/apollohg/editor/EditorEditTextTextCommit.kt +327 -0
  52. package/android/src/main/java/com/apollohg/editor/EditorEditTextToolbar.kt +84 -0
  53. package/android/src/main/java/com/apollohg/editor/EditorEditTextUpdates.kt +245 -0
  54. package/android/src/main/java/com/apollohg/editor/EditorGlyphHeightCursorDrawable.kt +42 -0
  55. package/android/src/main/java/com/apollohg/editor/EditorHeightBehavior.kt +13 -0
  56. package/android/src/main/java/com/apollohg/editor/EditorInputConnection.kt +923 -0
  57. package/android/src/main/java/com/apollohg/editor/EditorInputCoordinates.kt +191 -0
  58. package/android/src/main/java/com/apollohg/editor/EditorInputCorrection.kt +107 -0
  59. package/android/src/main/java/com/apollohg/editor/EditorInputCursorGeometry.kt +128 -0
  60. package/android/src/main/java/com/apollohg/editor/EditorInputSnapshots.kt +134 -0
  61. package/android/src/main/java/com/apollohg/editor/EditorInputSurroundingDeletion.kt +321 -0
  62. package/android/src/main/java/com/apollohg/editor/EditorKeyboardToolbarView.kt +851 -0
  63. package/android/src/main/java/com/apollohg/editor/EditorMentionStyle.kt +192 -0
  64. package/android/src/main/java/com/apollohg/editor/EditorReconciliationWatcher.kt +46 -0
  65. package/android/src/main/java/com/apollohg/editor/EditorStyleSheet.kt +303 -0
  66. package/android/src/main/java/com/apollohg/editor/EditorStyleSheetSpans.kt +253 -0
  67. package/android/src/main/java/com/apollohg/editor/EditorSurfaceInputConnection.kt +67 -0
  68. package/android/src/main/java/com/apollohg/editor/EditorTextDecorationDrawing.kt +143 -0
  69. package/android/src/main/java/com/apollohg/editor/EditorTextSurface.kt +712 -0
  70. package/android/src/main/java/com/apollohg/editor/EditorTextSurfaceDragDrop.kt +94 -0
  71. package/android/src/main/java/com/apollohg/editor/EditorTextSurfaceInteraction.kt +708 -0
  72. package/android/src/main/java/com/apollohg/editor/EditorTheme.kt +650 -0
  73. package/android/src/main/java/com/apollohg/editor/EditorV2Adapter.kt +912 -0
  74. package/android/src/main/java/com/apollohg/editor/EditorV2AdapterMutations.kt +402 -0
  75. package/android/src/main/java/com/apollohg/editor/EditorV2AdapterRendering.kt +123 -0
  76. package/android/src/main/java/com/apollohg/editor/EditorV2AdapterSelection.kt +203 -0
  77. package/android/src/main/java/com/apollohg/editor/EditorV2Backend.kt +120 -0
  78. package/android/src/main/java/com/apollohg/editor/EditorV2Driver.kt +87 -0
  79. package/android/src/main/java/com/apollohg/editor/EditorV2Registry.kt +97 -0
  80. package/android/src/main/java/com/apollohg/editor/EditorV2RenderValidation.kt +409 -0
  81. package/android/src/main/java/com/apollohg/editor/ExpoAutoGrowStyleSizePublisher.kt +34 -0
  82. package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +266 -0
  83. package/android/src/main/java/com/apollohg/editor/ImeTextCoordinateMapper.kt +102 -0
  84. package/android/src/main/java/com/apollohg/editor/KeyboardToolbarImeAnimationController.kt +101 -0
  85. package/android/src/main/java/com/apollohg/editor/MentionSuggestionChipView.kt +234 -0
  86. package/android/src/main/java/com/apollohg/editor/NativeCollaborationTransportRegistry.kt +493 -0
  87. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +813 -0
  88. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewAtoms.kt +68 -0
  89. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewBinding.kt +126 -0
  90. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewDiagnostics.kt +208 -0
  91. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewErrors.kt +146 -0
  92. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewEvents.kt +150 -0
  93. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewExternalComposition.kt +39 -0
  94. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewFocus.kt +176 -0
  95. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewLifecycle.kt +302 -0
  96. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewMentions.kt +259 -0
  97. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewNativeActions.kt +179 -0
  98. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewOutsideTap.kt +214 -0
  99. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewPendingUpdates.kt +376 -0
  100. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewProperties.kt +234 -0
  101. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewPropertyRetries.kt +197 -0
  102. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewToolbar.kt +285 -0
  103. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewTypes.kt +28 -0
  104. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewUpdates.kt +271 -0
  105. package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +687 -0
  106. package/android/src/main/java/com/apollohg/editor/NativeEditorOutsideTapDispatcher.kt +380 -0
  107. package/android/src/main/java/com/apollohg/editor/NativeEditorV2Numbers.kt +45 -0
  108. package/android/src/main/java/com/apollohg/editor/NativeEditorViewRegistry.kt +372 -0
  109. package/android/src/main/java/com/apollohg/editor/NativeImagePipeline.kt +22 -0
  110. package/android/src/main/java/com/apollohg/editor/NativeToolbar.kt +193 -0
  111. package/android/src/main/java/com/apollohg/editor/NativeToolbarIcons.kt +177 -0
  112. package/android/src/main/java/com/apollohg/editor/NativeToolbarItem.kt +274 -0
  113. package/android/src/main/java/com/apollohg/editor/OkHttpCollaborationSocket.kt +102 -0
  114. package/android/src/main/java/com/apollohg/editor/OrderedListMarkerFormatter.kt +130 -0
  115. package/android/src/main/java/com/apollohg/editor/PointerTransparentView.java +18 -0
  116. package/android/src/main/java/com/apollohg/editor/PositionBridge.kt +128 -0
  117. package/android/src/main/java/com/apollohg/editor/ProseViewerView.kt +854 -0
  118. package/android/src/main/java/com/apollohg/editor/RemoteSelectionOverlayView.kt +314 -0
  119. package/android/src/main/java/com/apollohg/editor/RenderBlockSpans.kt +314 -0
  120. package/android/src/main/java/com/apollohg/editor/RenderBridge.kt +224 -0
  121. package/android/src/main/java/com/apollohg/editor/RenderBridgeBlockStyles.kt +407 -0
  122. package/android/src/main/java/com/apollohg/editor/RenderBridgeElements.kt +544 -0
  123. package/android/src/main/java/com/apollohg/editor/RenderBridgeInlineAndAtoms.kt +631 -0
  124. package/android/src/main/java/com/apollohg/editor/RenderBridgeParagraphs.kt +215 -0
  125. package/android/src/main/java/com/apollohg/editor/RenderImageDecoder.kt +748 -0
  126. package/android/src/main/java/com/apollohg/editor/RenderImageLoader.kt +900 -0
  127. package/android/src/main/java/com/apollohg/editor/RenderLayoutConstants.kt +117 -0
  128. package/android/src/main/java/com/apollohg/editor/RenderMediaSpans.kt +337 -0
  129. package/android/src/main/java/com/apollohg/editor/RenderParagraphSpans.kt +182 -0
  130. package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +951 -0
  131. package/android/src/main/java/com/apollohg/editor/SharedNativeImagePipeline.kt +78 -0
  132. package/android/src/main/java/com/apollohg/editor/UniffiEditorV2Backend.kt +261 -0
  133. package/android/src/main/java/com/apollohg/editor/viewer/AndroidProseLayoutEngine.kt +1656 -0
  134. package/android/src/main/java/com/apollohg/editor/viewer/FabricAttachmentSidecars.kt +80 -0
  135. package/android/src/main/java/com/apollohg/editor/viewer/FabricLeaseHandleBridge.kt +59 -0
  136. package/android/src/main/java/com/apollohg/editor/viewer/FallbackSelectionGeometry.kt +450 -0
  137. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseDrawingView.kt +678 -0
  138. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseInstrumentation.kt +608 -0
  139. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseLayout.kt +186 -0
  140. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseLayoutCache.kt +393 -0
  141. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseLayoutRegistry.kt +828 -0
  142. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseTheme.kt +316 -0
  143. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseViewerManager.kt +900 -0
  144. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseViewerPackage.kt +16 -0
  145. package/android/src/main/java/com/apollohg/editor/viewer/PreparedViewerAtoms.kt +89 -0
  146. package/android/src/main/java/com/apollohg/editor/viewer/ViewerCodeHighlighting.kt +91 -0
  147. package/android/src/main/java/com/apollohg/editor/viewer/ViewerDocument.kt +453 -0
  148. package/android/src/main/java/com/apollohg/editor/viewer/ViewerFontEnvironment.kt +282 -0
  149. package/android/src/main/java/com/apollohg/editor/viewer/ViewerImagePipeline.kt +460 -0
  150. package/android/src/main/jni/CMakeLists.txt +85 -0
  151. package/android/src/main/jni/PreparedProseViewerMeasurementsManager.cpp +334 -0
  152. package/android/src/main/jni/ReactNativeProseEditorSpec.h +17 -0
  153. package/android/src/main/jni/reactnativeproseeditor.h +6 -0
  154. package/android/src/main/res/values/attrs.xml +6 -0
  155. package/android/src/release/java/com/apollohg/editor/viewer/PreparedProseDrawInstrumentation.kt +9 -0
  156. package/app.plugin.js +62 -0
  157. package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseMeasurementsManager.h +62 -0
  158. package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerComponentDescriptor.h +30 -0
  159. package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerShadowNode.cpp +192 -0
  160. package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerShadowNode.h +46 -0
  161. package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerState.h +145 -0
  162. package/dist/AtomHost.d.ts +17 -0
  163. package/dist/AtomHost.js +135 -0
  164. package/dist/DefaultAtomChip.d.ts +2 -0
  165. package/dist/DefaultAtomChip.js +22 -0
  166. package/dist/EditorAddon.d.ts +32 -0
  167. package/dist/EditorAddon.js +29 -0
  168. package/dist/EditorAddonNormalization.d.ts +6 -0
  169. package/dist/EditorAddonNormalization.js +48 -0
  170. package/dist/EditorMentionThemeNormalization.d.ts +8 -0
  171. package/dist/EditorMentionThemeNormalization.js +22 -0
  172. package/dist/EditorMentionThemeValidation.d.ts +12 -0
  173. package/dist/EditorMentionThemeValidation.js +95 -0
  174. package/dist/EditorStyleSheet.d.ts +4 -0
  175. package/dist/EditorStyleSheet.js +10 -0
  176. package/dist/EditorStyleSheetNormalization.d.ts +3 -0
  177. package/dist/EditorStyleSheetNormalization.js +226 -0
  178. package/dist/EditorStyleSheetTypes.d.ts +151 -0
  179. package/dist/EditorStyleSheetTypes.js +2 -0
  180. package/dist/EditorTheme.d.ts +117 -0
  181. package/dist/EditorTheme.js +41 -0
  182. package/dist/EditorToolbar.d.ts +4 -0
  183. package/dist/EditorToolbar.js +17 -0
  184. package/dist/EditorToolbarComponent.d.ts +13 -0
  185. package/dist/EditorToolbarComponent.js +24 -0
  186. package/dist/EditorToolbarItems.d.ts +9 -0
  187. package/dist/EditorToolbarItems.js +58 -0
  188. package/dist/EditorToolbarRegistry.d.ts +59 -0
  189. package/dist/EditorToolbarRegistry.js +175 -0
  190. package/dist/EditorToolbarTypes.d.ts +273 -0
  191. package/dist/EditorToolbarTypes.js +2 -0
  192. package/dist/EditorToolbarVisuals.d.ts +154 -0
  193. package/dist/EditorToolbarVisuals.js +228 -0
  194. package/dist/EditorUpdateRevision.d.ts +8 -0
  195. package/dist/EditorUpdateRevision.js +22 -0
  196. package/dist/ExternalTextComposition.d.ts +45 -0
  197. package/dist/ExternalTextComposition.js +537 -0
  198. package/dist/ImageLoadingPolicy.d.ts +54 -0
  199. package/dist/ImageLoadingPolicy.js +60 -0
  200. package/dist/NativeEditorBoundaryError.d.ts +111 -0
  201. package/dist/NativeEditorBoundaryError.js +268 -0
  202. package/dist/NativeEditorBridge.d.ts +9 -0
  203. package/dist/NativeEditorBridge.js +37 -0
  204. package/dist/NativeEditorCollaborationTransport.d.ts +154 -0
  205. package/dist/NativeEditorCollaborationTransport.js +275 -0
  206. package/dist/NativeEditorCreateConfig.d.ts +16 -0
  207. package/dist/NativeEditorCreateConfig.js +249 -0
  208. package/dist/NativeEditorCreateJson.d.ts +76 -0
  209. package/dist/NativeEditorCreateJson.js +474 -0
  210. package/dist/NativeEditorCreateValidation.d.ts +32 -0
  211. package/dist/NativeEditorCreateValidation.js +150 -0
  212. package/dist/NativeEditorDocumentBridge.d.ts +72 -0
  213. package/dist/NativeEditorDocumentBridge.js +305 -0
  214. package/dist/NativeEditorDocumentHandle.d.ts +70 -0
  215. package/dist/NativeEditorDocumentHandle.js +89 -0
  216. package/dist/NativeEditorLocalAwareness.d.ts +34 -0
  217. package/dist/NativeEditorLocalAwareness.js +153 -0
  218. package/dist/NativeEditorNativeModule.d.ts +48 -0
  219. package/dist/NativeEditorNativeModule.js +34 -0
  220. package/dist/NativeEditorRenderNormalization.d.ts +22 -0
  221. package/dist/NativeEditorRenderNormalization.js +274 -0
  222. package/dist/NativeEditorResultNormalization.d.ts +108 -0
  223. package/dist/NativeEditorResultNormalization.js +400 -0
  224. package/dist/NativeEditorTypes.d.ts +318 -0
  225. package/dist/NativeEditorTypes.js +15 -0
  226. package/dist/NativeEditorV2Decimal.d.ts +5 -0
  227. package/dist/NativeEditorV2Decimal.js +18 -0
  228. package/dist/NativeProseViewer.d.ts +122 -0
  229. package/dist/NativeProseViewer.js +130 -0
  230. package/dist/NativeRichTextEditor.d.ts +3 -0
  231. package/dist/NativeRichTextEditor.js +6 -0
  232. package/dist/ResourceLimits.d.ts +100 -0
  233. package/dist/ResourceLimits.js +80 -0
  234. package/dist/RichTextEditor.d.ts +4 -0
  235. package/dist/RichTextEditor.js +25 -0
  236. package/dist/RichTextEditorNativeTypes.d.ts +122 -0
  237. package/dist/RichTextEditorNativeTypes.js +2 -0
  238. package/dist/RichTextEditorNativeView.d.ts +12 -0
  239. package/dist/RichTextEditorNativeView.js +15 -0
  240. package/dist/RichTextEditorSerialization.d.ts +53 -0
  241. package/dist/RichTextEditorSerialization.js +283 -0
  242. package/dist/RichTextEditorTypes.d.ts +281 -0
  243. package/dist/RichTextEditorTypes.js +2 -0
  244. package/dist/ViewerConfiguration.d.ts +22 -0
  245. package/dist/ViewerConfiguration.js +6 -0
  246. package/dist/YjsCollaboration.d.ts +146 -0
  247. package/dist/YjsCollaboration.js +496 -0
  248. package/dist/addons.d.ts +193 -0
  249. package/dist/addons.js +122 -0
  250. package/dist/atomConstants.d.ts +3 -0
  251. package/dist/atomConstants.js +6 -0
  252. package/dist/atomInstances.d.ts +35 -0
  253. package/dist/atomInstances.js +79 -0
  254. package/dist/atomPolicy.d.ts +4 -0
  255. package/dist/atomPolicy.js +47 -0
  256. package/dist/atomUpdates.d.ts +2 -0
  257. package/dist/atomUpdates.js +38 -0
  258. package/dist/atoms.d.ts +92 -0
  259. package/dist/atoms.js +222 -0
  260. package/dist/attributeValidation.d.ts +4 -0
  261. package/dist/attributeValidation.js +97 -0
  262. package/dist/contentExpression.d.ts +6 -0
  263. package/dist/contentExpression.js +335 -0
  264. package/dist/index.d.ts +21 -0
  265. package/dist/index.js +78 -0
  266. package/dist/listMarkers.d.ts +15 -0
  267. package/dist/listMarkers.js +21 -0
  268. package/dist/schemaDefinition.d.ts +143 -0
  269. package/dist/schemaDefinition.js +206 -0
  270. package/dist/schemaDocument.d.ts +17 -0
  271. package/dist/schemaDocument.js +69 -0
  272. package/dist/schemaDocumentConstruction.d.ts +5 -0
  273. package/dist/schemaDocumentConstruction.js +125 -0
  274. package/dist/schemaNormalization.d.ts +29 -0
  275. package/dist/schemaNormalization.js +313 -0
  276. package/dist/schemaPresets.d.ts +68 -0
  277. package/dist/schemaPresets.js +198 -0
  278. package/dist/schemaResolution.d.ts +9 -0
  279. package/dist/schemaResolution.js +211 -0
  280. package/dist/schemas.d.ts +5 -0
  281. package/dist/schemas.js +28 -0
  282. package/dist/specs/PreparedProseViewerNativeComponent.d.ts +37 -0
  283. package/dist/specs/PreparedProseViewerNativeComponent.js +6 -0
  284. package/dist/useEditorToolbarInteractions.d.ts +12 -0
  285. package/dist/useEditorToolbarInteractions.js +218 -0
  286. package/dist/useEditorToolbarItems.d.ts +8 -0
  287. package/dist/useEditorToolbarItems.js +280 -0
  288. package/dist/useEditorToolbarPresentation.d.ts +4 -0
  289. package/dist/useEditorToolbarPresentation.js +203 -0
  290. package/dist/useEditorToolbarState.d.ts +63 -0
  291. package/dist/useEditorToolbarState.js +108 -0
  292. package/dist/useFocusPreservingFrames.d.ts +17 -0
  293. package/dist/useFocusPreservingFrames.js +108 -0
  294. package/dist/useNativeEditor.d.ts +66 -0
  295. package/dist/useNativeEditor.js +370 -0
  296. package/dist/useRichTextEditorCommands.d.ts +21 -0
  297. package/dist/useRichTextEditorCommands.js +299 -0
  298. package/dist/useRichTextEditorEvents.d.ts +18 -0
  299. package/dist/useRichTextEditorEvents.js +216 -0
  300. package/dist/useRichTextEditorMentions.d.ts +11 -0
  301. package/dist/useRichTextEditorMentions.js +316 -0
  302. package/dist/useRichTextEditorPresentation.d.ts +5 -0
  303. package/dist/useRichTextEditorPresentation.js +178 -0
  304. package/dist/useRichTextEditorState.d.ts +180 -0
  305. package/dist/useRichTextEditorState.js +405 -0
  306. package/dist/useRichTextEditorUpdates.d.ts +10 -0
  307. package/dist/useRichTextEditorUpdates.js +308 -0
  308. package/dist/useViewerAtoms.d.ts +38 -0
  309. package/dist/useViewerAtoms.js +288 -0
  310. package/expo-module.config.json +13 -0
  311. package/ios/EditorAddons.swift +354 -0
  312. package/ios/EditorCodeHighlighting.swift +184 -0
  313. package/ios/EditorCore.xcframework/Info.plist +44 -0
  314. package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
  315. package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
  316. package/ios/EditorInteractionOverlays.swift +697 -0
  317. package/ios/EditorLayoutManager.swift +971 -0
  318. package/ios/EditorNodeTypes.swift +37 -0
  319. package/ios/EditorStyleSheet.swift +461 -0
  320. package/ios/EditorTextView+AttributedPatch.swift +305 -0
  321. package/ios/EditorTextView+Binding.swift +61 -0
  322. package/ios/EditorTextView+Caret.swift +333 -0
  323. package/ios/EditorTextView+Commands.swift +203 -0
  324. package/ios/EditorTextView+Delegate.swift +65 -0
  325. package/ios/EditorTextView+Diagnostics.swift +225 -0
  326. package/ios/EditorTextView+DragDrop.swift +290 -0
  327. package/ios/EditorTextView+ExternalComposition.swift +364 -0
  328. package/ios/EditorTextView+Images.swift +354 -0
  329. package/ios/EditorTextView+InputTraits.swift +200 -0
  330. package/ios/EditorTextView+KeyboardInput.swift +318 -0
  331. package/ios/EditorTextView+Layout.swift +245 -0
  332. package/ios/EditorTextView+MarkedText.swift +267 -0
  333. package/ios/EditorTextView+NativeInputLifecycle.swift +372 -0
  334. package/ios/EditorTextView+NativeMutation.swift +471 -0
  335. package/ios/EditorTextView+Reconciliation.swift +97 -0
  336. package/ios/EditorTextView+RenderPatch.swift +573 -0
  337. package/ios/EditorTextView+Rendering.swift +539 -0
  338. package/ios/EditorTextView+Selection.swift +542 -0
  339. package/ios/EditorTextView.swift +953 -0
  340. package/ios/EditorTheme.swift +626 -0
  341. package/ios/EditorV2Adapter+Commands.swift +204 -0
  342. package/ios/EditorV2Adapter+Envelope.swift +238 -0
  343. package/ios/EditorV2Adapter+Mutation.swift +392 -0
  344. package/ios/EditorV2Adapter+Registry.swift +185 -0
  345. package/ios/EditorV2Adapter+RenderState.swift +360 -0
  346. package/ios/EditorV2Adapter+RenderValidation.swift +420 -0
  347. package/ios/EditorV2Adapter+Selection.swift +329 -0
  348. package/ios/EditorV2Adapter+TextCommands.swift +430 -0
  349. package/ios/EditorV2Adapter.swift +376 -0
  350. package/ios/EditorV2Shadow.swift +353 -0
  351. package/ios/Generated_editor_core.swift +2378 -0
  352. package/ios/NativeCodeHighlighting.swift +159 -0
  353. package/ios/NativeCollaborationSocket.swift +144 -0
  354. package/ios/NativeCollaborationTransport+Protocol.swift +258 -0
  355. package/ios/NativeCollaborationTransport+Socket.swift +379 -0
  356. package/ios/NativeCollaborationTransport+Validation.swift +103 -0
  357. package/ios/NativeCollaborationTransport.swift +322 -0
  358. package/ios/NativeCollaborationTransportRegistry.swift +521 -0
  359. package/ios/NativeEditorExpoView+AccessoryRetry.swift +219 -0
  360. package/ios/NativeEditorExpoView+Binding.swift +268 -0
  361. package/ios/NativeEditorExpoView+Diagnostics.swift +66 -0
  362. package/ios/NativeEditorExpoView+Events.swift +131 -0
  363. package/ios/NativeEditorExpoView+Focus.swift +169 -0
  364. package/ios/NativeEditorExpoView+MentionRetry.swift +288 -0
  365. package/ios/NativeEditorExpoView+Mentions.swift +288 -0
  366. package/ios/NativeEditorExpoView+Properties.swift +300 -0
  367. package/ios/NativeEditorExpoView+Toolbar.swift +86 -0
  368. package/ios/NativeEditorExpoView+Updates.swift +312 -0
  369. package/ios/NativeEditorExpoView.swift +349 -0
  370. package/ios/NativeEditorModule.swift +856 -0
  371. package/ios/NativeEditorViewRegistry.swift +226 -0
  372. package/ios/NativeToolbar+ButtonStyle.swift +214 -0
  373. package/ios/NativeToolbar+Buttons.swift +228 -0
  374. package/ios/NativeToolbar+Diagnostics.swift +114 -0
  375. package/ios/NativeToolbar+Menu.swift +101 -0
  376. package/ios/NativeToolbar+Models.swift +519 -0
  377. package/ios/NativeToolbar+Placeholder.swift +39 -0
  378. package/ios/NativeToolbar.swift +612 -0
  379. package/ios/OrderedListMarkerFormatter.swift +103 -0
  380. package/ios/PositionBridge.swift +596 -0
  381. package/ios/ProseViewerView.swift +462 -0
  382. package/ios/RenderBridge+Atoms.swift +288 -0
  383. package/ios/RenderBridge+Attachments.swift +246 -0
  384. package/ios/RenderBridge+BlockBoundaries.swift +313 -0
  385. package/ios/RenderBridge+Style.swift +343 -0
  386. package/ios/RenderBridge.swift +704 -0
  387. package/ios/RichTextEditorView.swift +930 -0
  388. package/ios/SharedNativeImagePipeline+Cache.swift +189 -0
  389. package/ios/SharedNativeImagePipeline+Transport.swift +328 -0
  390. package/ios/SharedNativeImagePipeline.swift +533 -0
  391. package/ios/Viewer/CoreTextProseLayoutEngine+AttributedText.swift +436 -0
  392. package/ios/Viewer/CoreTextProseLayoutEngine+Models.swift +322 -0
  393. package/ios/Viewer/CoreTextProseLayoutEngine.swift +635 -0
  394. package/ios/Viewer/Fabric/PREPPreparedProseViewerComponentView.h +4 -0
  395. package/ios/Viewer/Fabric/PREPPreparedProseViewerComponentView.mm +767 -0
  396. package/ios/Viewer/Fabric/PreparedProseMeasurementsManager.mm +118 -0
  397. package/ios/Viewer/NativeSVGImageDecoder.h +9 -0
  398. package/ios/Viewer/NativeSVGImageDecoder.mm +106 -0
  399. package/ios/Viewer/NativeSVGPreflight.h +11 -0
  400. package/ios/Viewer/NativeSVGPreflight.mm +219 -0
  401. package/ios/Viewer/PreparedProseDrawingView.swift +639 -0
  402. package/ios/Viewer/PreparedProseInstrumentation.swift +387 -0
  403. package/ios/Viewer/PreparedProseLayout.swift +326 -0
  404. package/ios/Viewer/PreparedProseLayoutCache.swift +766 -0
  405. package/ios/Viewer/PreparedProseLayoutRegistry+Artifacts.swift +377 -0
  406. package/ios/Viewer/PreparedProseLayoutRegistry+Fabric.swift +582 -0
  407. package/ios/Viewer/PreparedProseLayoutRegistry+FabricOwnership.swift +128 -0
  408. package/ios/Viewer/PreparedProseLayoutRegistry.swift +381 -0
  409. package/ios/Viewer/ViewerDocument.swift +575 -0
  410. package/ios/Viewer/ViewerFontEnvironment.swift +283 -0
  411. package/ios/Viewer/ViewerImagePipeline.swift +478 -0
  412. package/ios/editor_coreFFI/editor_coreFFI.h +1023 -0
  413. package/ios/editor_coreFFI/module.modulemap +7 -0
  414. package/package.json +151 -0
  415. package/react-native.config.js +17 -0
  416. package/rust/android/arm64-v8a/libeditor_core.so +0 -0
  417. package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
  418. package/rust/android/x86/libeditor_core.so +0 -0
  419. package/rust/android/x86_64/libeditor_core.so +0 -0
  420. package/rust/bindings/kotlin/uniffi/editor_core/editor_core.kt +3146 -0
  421. package/src/AtomHost.tsx +180 -0
  422. package/src/DefaultAtomChip.tsx +27 -0
  423. package/src/EditorAddon.ts +65 -0
  424. package/src/EditorAddonNormalization.ts +49 -0
  425. package/src/EditorMentionThemeNormalization.ts +25 -0
  426. package/src/EditorMentionThemeValidation.ts +133 -0
  427. package/src/EditorStyleSheet.ts +9 -0
  428. package/src/EditorStyleSheetNormalization.ts +219 -0
  429. package/src/EditorStyleSheetTypes.ts +189 -0
  430. package/src/EditorTheme.ts +196 -0
  431. package/src/EditorToolbar.tsx +31 -0
  432. package/src/EditorToolbarComponent.tsx +23 -0
  433. package/src/EditorToolbarItems.tsx +64 -0
  434. package/src/EditorToolbarRegistry.tsx +254 -0
  435. package/src/EditorToolbarTypes.tsx +322 -0
  436. package/src/EditorToolbarVisuals.tsx +270 -0
  437. package/src/EditorUpdateRevision.ts +26 -0
  438. package/src/ExternalTextComposition.ts +658 -0
  439. package/src/ImageLoadingPolicy.ts +125 -0
  440. package/src/NativeEditorBoundaryError.ts +345 -0
  441. package/src/NativeEditorBridge.ts +93 -0
  442. package/src/NativeEditorCollaborationTransport.ts +493 -0
  443. package/src/NativeEditorCreateConfig.ts +326 -0
  444. package/src/NativeEditorCreateJson.ts +579 -0
  445. package/src/NativeEditorCreateValidation.ts +178 -0
  446. package/src/NativeEditorDocumentBridge.ts +485 -0
  447. package/src/NativeEditorDocumentHandle.ts +168 -0
  448. package/src/NativeEditorLocalAwareness.ts +168 -0
  449. package/src/NativeEditorNativeModule.ts +97 -0
  450. package/src/NativeEditorRenderNormalization.ts +320 -0
  451. package/src/NativeEditorResultNormalization.ts +469 -0
  452. package/src/NativeEditorTypes.ts +366 -0
  453. package/src/NativeEditorV2Decimal.ts +13 -0
  454. package/src/NativeProseViewer.tsx +356 -0
  455. package/src/NativeRichTextEditor.tsx +33 -0
  456. package/src/ResourceLimits.ts +187 -0
  457. package/src/RichTextEditor.tsx +27 -0
  458. package/src/RichTextEditorNativeTypes.tsx +144 -0
  459. package/src/RichTextEditorNativeView.tsx +21 -0
  460. package/src/RichTextEditorSerialization.tsx +381 -0
  461. package/src/RichTextEditorTypes.tsx +327 -0
  462. package/src/ViewerConfiguration.ts +23 -0
  463. package/src/YjsCollaboration.ts +692 -0
  464. package/src/addons.ts +311 -0
  465. package/src/atomConstants.ts +3 -0
  466. package/src/atomInstances.ts +123 -0
  467. package/src/atomPolicy.ts +47 -0
  468. package/src/atomUpdates.ts +52 -0
  469. package/src/atoms.ts +379 -0
  470. package/src/attributeValidation.ts +116 -0
  471. package/src/contentExpression.ts +338 -0
  472. package/src/index.ts +295 -0
  473. package/src/listMarkers.ts +18 -0
  474. package/src/schemaDefinition.ts +395 -0
  475. package/src/schemaDocument.ts +92 -0
  476. package/src/schemaDocumentConstruction.ts +148 -0
  477. package/src/schemaNormalization.ts +367 -0
  478. package/src/schemaPresets.ts +250 -0
  479. package/src/schemaResolution.ts +242 -0
  480. package/src/schemas.ts +43 -0
  481. package/src/specs/PreparedProseViewerNativeComponent.ts +40 -0
  482. package/src/useEditorToolbarInteractions.tsx +329 -0
  483. package/src/useEditorToolbarItems.tsx +390 -0
  484. package/src/useEditorToolbarPresentation.tsx +483 -0
  485. package/src/useEditorToolbarState.tsx +172 -0
  486. package/src/useFocusPreservingFrames.ts +171 -0
  487. package/src/useNativeEditor.ts +534 -0
  488. package/src/useRichTextEditorCommands.tsx +478 -0
  489. package/src/useRichTextEditorEvents.tsx +342 -0
  490. package/src/useRichTextEditorMentions.tsx +441 -0
  491. package/src/useRichTextEditorPresentation.tsx +563 -0
  492. package/src/useRichTextEditorState.tsx +620 -0
  493. package/src/useRichTextEditorUpdates.tsx +403 -0
  494. package/src/useViewerAtoms.tsx +427 -0
@@ -0,0 +1,748 @@
1
+ package com.apollohg.editor
2
+
3
+ import android.graphics.Bitmap
4
+ import android.graphics.BitmapFactory
5
+ import android.graphics.Canvas
6
+ import android.util.Base64
7
+ import android.util.Log
8
+ import android.util.Xml
9
+ import com.caverock.androidsvg.SVG
10
+ import java.io.ByteArrayOutputStream
11
+ import java.io.InputStream
12
+ import java.net.HttpURLConnection
13
+ import java.net.URL
14
+ import java.util.concurrent.atomic.AtomicBoolean
15
+ import org.xmlpull.v1.XmlPullParser
16
+
17
+ internal object RenderImageDecoder {
18
+ internal const val LOG_TAG = "NativeEditorImage"
19
+
20
+ @Volatile
21
+ internal var connectionFactoryOverride: ((URL) -> HttpURLConnection)? = null
22
+
23
+ @Volatile
24
+ internal var bitmapDecoderOverride: ((ByteArray, ImageLoadingPolicy) -> Bitmap?)? = null
25
+
26
+ internal data class DataUrlAdmission(
27
+ val commaIndex: Int,
28
+ val maximumEncodedCharacters: Long,
29
+ val sanitizedPayloadLength: Int
30
+ )
31
+
32
+ internal class Cancellation {
33
+ private val cancelled = AtomicBoolean(false)
34
+
35
+ @Volatile private var connection: HttpURLConnection? = null
36
+
37
+ @Volatile private var stream: InputStream? = null
38
+
39
+ fun isCancelled(): Boolean = cancelled.get()
40
+
41
+ fun bind(connection: HttpURLConnection) {
42
+ this.connection = connection
43
+ if (cancelled.get()) runCatching { connection.disconnect() }
44
+ }
45
+
46
+ fun bind(stream: InputStream) {
47
+ this.stream = stream
48
+ if (cancelled.get()) runCatching { stream.close() }
49
+ }
50
+
51
+ fun cancel() {
52
+ if (!cancelled.compareAndSet(false, true)) return
53
+ runCatching { connection?.disconnect() }
54
+ runCatching { stream?.close() }
55
+ }
56
+ }
57
+
58
+ internal fun decodeSourceLease(
59
+ source: String,
60
+ policy: ImageLoadingPolicy = ImageLoadingPolicy.DEFAULT,
61
+ cancellation: Cancellation? = null,
62
+ clock: MonotonicClock = systemMonotonicClock,
63
+ deadlineMs: Long = deadlineAfter(clock.elapsedRealtime(), policy.requestTimeoutMs),
64
+ priority: DecodedBitmapPriority = DecodedBitmapPriority.VISIBLE
65
+ ): DecodedBitmapLease? {
66
+ if (unavailable(cancellation, clock, deadlineMs)) return null
67
+ if (source.regionMatches(0, "data:image/", 0, "data:image/".length, ignoreCase = true)) {
68
+ val bytes = decodeDataUrlBytes(source, policy, cancellation) ?: return null
69
+ if (unavailable(cancellation, clock, deadlineMs)) return null
70
+ val decoded = decodeBitmapLease(bytes, policy, priority)
71
+ if (decoded == null) {
72
+ Log.w(
73
+ LOG_TAG,
74
+ "decodeSource: failed to decode data URL bytes (${sourceSummary(source)})"
75
+ )
76
+ } else {
77
+ Log.d(
78
+ LOG_TAG,
79
+ "decodeSource: decoded data URL ${sourceSummary(
80
+ source
81
+ )} -> ${decoded.bitmap.width}x${decoded.bitmap.height}"
82
+ )
83
+ }
84
+ return decoded
85
+ }
86
+ val connection = runCatching {
87
+ connectionFactoryOverride?.invoke(URL(source))
88
+ ?: (URL(source).openConnection() as HttpURLConnection)
89
+ }.getOrNull() ?: return null
90
+ cancellation?.bind(connection)
91
+ val remoteBytes = try {
92
+ if (unavailable(cancellation, clock, deadlineMs)) return null
93
+ connection.connectTimeout = boundedTransportTimeout(
94
+ policy.connectTimeoutMs,
95
+ clock,
96
+ deadlineMs
97
+ )
98
+ connection.readTimeout =
99
+ boundedTransportTimeout(policy.readTimeoutMs, clock, deadlineMs)
100
+ connection.instanceFollowRedirects = true
101
+ val status = connection.responseCode
102
+ if (unavailable(cancellation, clock, deadlineMs) || status !in 200..299 ||
103
+ connection.contentLengthLong > policy.maxSourceBytes
104
+ ) {
105
+ null
106
+ } else {
107
+ connection.inputStream.use { input ->
108
+ cancellation?.bind(input)
109
+ readBounded(input, policy, clock, cancellation, deadlineMs)
110
+ }
111
+ }
112
+ } catch (_: Exception) {
113
+ null
114
+ } finally {
115
+ connection.disconnect()
116
+ } ?: run {
117
+ Log.w(LOG_TAG, "decodeSource: failed to load remote image (${sourceSummary(source)})")
118
+ return null
119
+ }
120
+ if (unavailable(cancellation, clock, deadlineMs)) return null
121
+ val decoded = decodeBitmapLease(remoteBytes, policy, priority)
122
+ if (decoded == null) {
123
+ Log.w(LOG_TAG, "decodeSource: failed to decode remote bytes (${sourceSummary(source)})")
124
+ }
125
+ return decoded
126
+ }
127
+
128
+ fun decodeDataUrlBytes(
129
+ source: String,
130
+ policy: ImageLoadingPolicy = ImageLoadingPolicy.DEFAULT,
131
+ cancellation: Cancellation? = null
132
+ ): ByteArray? {
133
+ val admission = preflightDataUrl(source, policy, cancellation) ?: return null
134
+ val commaIndex = admission.commaIndex
135
+ val maximumEncodedCharacters = admission.maximumEncodedCharacters
136
+ val payload = StringBuilder(admission.sanitizedPayloadLength)
137
+ for (payloadIndex in commaIndex + 1 until source.length) {
138
+ if (cancellation?.isCancelled() == true) return null
139
+ val character = source[payloadIndex]
140
+ if (!character.isWhitespace()) {
141
+ payload.append(character)
142
+ if (payload.length.toLong() > maximumEncodedCharacters) return null
143
+ }
144
+ }
145
+
146
+ val decodeFlags = intArrayOf(
147
+ Base64.DEFAULT,
148
+ Base64.NO_WRAP,
149
+ Base64.URL_SAFE or Base64.NO_WRAP,
150
+ Base64.URL_SAFE
151
+ )
152
+ for (flags in decodeFlags) {
153
+ val bytes = runCatching { Base64.decode(payload.toString(), flags) }.getOrNull()
154
+ if (bytes != null && bytes.size <= policy.maxSourceBytes) {
155
+ return bytes
156
+ }
157
+ }
158
+ Log.w(LOG_TAG, "decodeDataUrlBytes: unsupported base64 payload (${sourceSummary(source)})")
159
+ return null
160
+ }
161
+
162
+ internal fun preflightDataUrl(
163
+ source: String,
164
+ policy: ImageLoadingPolicy,
165
+ cancellation: Cancellation? = null
166
+ ): DataUrlAdmission? {
167
+ if (cancellation?.isCancelled() == true ||
168
+ !source.regionMatches(0, "data:image/", 0, "data:image/".length, ignoreCase = true)
169
+ ) {
170
+ return null
171
+ }
172
+ var commaIndex = -1
173
+ var metadataUtf8Bytes = 0
174
+ var index = 0
175
+ while (index < source.length) {
176
+ if (cancellation?.isCancelled() == true) return null
177
+ val character = source[index]
178
+ if (character == ',') {
179
+ commaIndex = index
180
+ break
181
+ }
182
+ metadataUtf8Bytes += utf8BytesAt(source, index)
183
+ if (metadataUtf8Bytes > MAX_DATA_URL_METADATA_BYTES) return null
184
+ index += if (Character.isHighSurrogate(character) &&
185
+ index + 1 < source.length && Character.isLowSurrogate(source[index + 1])
186
+ ) {
187
+ 2
188
+ } else {
189
+ 1
190
+ }
191
+ }
192
+ if (commaIndex <= 0) return null
193
+ if (!hasBase64MetadataToken(source, commaIndex)) return null
194
+ val maximumEncodedCharacters = ((policy.maxSourceBytes.toLong() + 2L) / 3L) * 4L
195
+ val maximumRawPayload = maximumEncodedCharacters + DATA_URL_WHITESPACE_ALLOWANCE_BYTES
196
+ val rawPayloadLength = source.length.toLong() - commaIndex.toLong() - 1L
197
+ if (rawPayloadLength > maximumRawPayload) return null
198
+ var sanitizedPayloadLength = 0L
199
+ for (payloadIndex in commaIndex + 1 until source.length) {
200
+ if (cancellation?.isCancelled() == true) return null
201
+ if (!source[payloadIndex].isWhitespace()) {
202
+ sanitizedPayloadLength += 1L
203
+ if (sanitizedPayloadLength > maximumEncodedCharacters) return null
204
+ }
205
+ }
206
+ return DataUrlAdmission(
207
+ commaIndex,
208
+ maximumEncodedCharacters,
209
+ sanitizedPayloadLength.toInt()
210
+ )
211
+ }
212
+
213
+ private fun hasBase64MetadataToken(source: String, commaIndex: Int): Boolean {
214
+ var index = "data:image/".length
215
+ while (index < commaIndex) {
216
+ if (source[index] == ';') {
217
+ val tokenStart = index + 1
218
+ val tokenEnd = tokenStart + "base64".length
219
+ if (tokenEnd <= commaIndex &&
220
+ source.regionMatches(
221
+ tokenStart,
222
+ "base64",
223
+ 0,
224
+ "base64".length,
225
+ ignoreCase = true
226
+ ) && (tokenEnd == commaIndex || source[tokenEnd] == ';')
227
+ ) {
228
+ return true
229
+ }
230
+ }
231
+ index += 1
232
+ }
233
+ return false
234
+ }
235
+
236
+ fun readBounded(
237
+ input: InputStream,
238
+ maxBytes: Int,
239
+ cancellation: Cancellation? = null
240
+ ): ByteArray? = readBoundedInternal(
241
+ input,
242
+ maxBytes,
243
+ cancellation,
244
+ systemMonotonicClock,
245
+ Long.MAX_VALUE
246
+ )
247
+
248
+ fun readBounded(
249
+ input: InputStream,
250
+ policy: ImageLoadingPolicy,
251
+ clock: MonotonicClock,
252
+ cancellation: Cancellation? = null,
253
+ deadlineMs: Long = deadlineAfter(clock.elapsedRealtime(), policy.requestTimeoutMs)
254
+ ): ByteArray? = readBoundedInternal(
255
+ input,
256
+ policy.maxSourceBytes,
257
+ cancellation,
258
+ clock,
259
+ deadlineMs
260
+ )
261
+
262
+ private fun readBoundedInternal(
263
+ input: InputStream,
264
+ maxBytes: Int,
265
+ cancellation: Cancellation?,
266
+ clock: MonotonicClock,
267
+ deadlineMs: Long
268
+ ): ByteArray? {
269
+ val output = ByteArrayOutputStream(minOf(maxBytes, 16 * 1024))
270
+ val buffer = ByteArray(8 * 1024)
271
+ var total = 0
272
+ while (true) {
273
+ if (unavailable(cancellation, clock, deadlineMs)) return null
274
+ val read = input.read(buffer)
275
+ if (unavailable(cancellation, clock, deadlineMs)) return null
276
+ if (read < 0) break
277
+ total += read
278
+ if (total > maxBytes) return null
279
+ output.write(buffer, 0, read)
280
+ }
281
+ return output.toByteArray()
282
+ }
283
+
284
+ private fun boundedTransportTimeout(
285
+ configuredMs: Int,
286
+ clock: MonotonicClock,
287
+ deadlineMs: Long
288
+ ): Int {
289
+ val remaining = (deadlineMs - clock.elapsedRealtime()).coerceAtLeast(1L)
290
+ return minOf(configuredMs.toLong(), remaining, Int.MAX_VALUE.toLong()).toInt()
291
+ }
292
+
293
+ private fun unavailable(
294
+ cancellation: Cancellation?,
295
+ clock: MonotonicClock,
296
+ deadlineMs: Long
297
+ ): Boolean = cancellation?.isCancelled() == true || clock.elapsedRealtime() >= deadlineMs
298
+
299
+ private fun utf8BytesAt(value: String, index: Int): Int {
300
+ val character = value[index]
301
+ return when {
302
+ character.code <= 0x7f -> 1
303
+
304
+ character.code <= 0x7ff -> 2
305
+
306
+ Character.isHighSurrogate(character) && index + 1 < value.length &&
307
+ Character.isLowSurrogate(value[index + 1]) -> 4
308
+
309
+ else -> 3
310
+ }
311
+ }
312
+
313
+ fun calculateInSampleSize(
314
+ width: Int,
315
+ height: Int,
316
+ maxWidth: Int = ImageLoadingPolicy.DEFAULT.maxDecodeDimensionPx,
317
+ maxHeight: Int = ImageLoadingPolicy.DEFAULT.maxDecodeDimensionPx,
318
+ maxDecodedBytes: Long = ImageLoadingPolicy.DEFAULT.maxDecodedBytes.toLong()
319
+ ): Int {
320
+ if (width <= 0 || height <= 0) return 1
321
+
322
+ if (maxWidth <= 0 || maxHeight <= 0) return 1
323
+ var sampleSize = 1L
324
+ var sampledWidth = width.toLong()
325
+ var sampledHeight = height.toLong()
326
+ while (
327
+ sampledWidth > maxWidth.toLong() ||
328
+ sampledHeight > maxHeight.toLong() ||
329
+ exceedsArgbBytes(sampledWidth, sampledHeight, maxDecodedBytes)
330
+ ) {
331
+ if (sampleSize >= (1L shl 30)) return (1 shl 30)
332
+ sampleSize = sampleSize shl 1
333
+ sampledWidth = (width.toLong() + sampleSize - 1L) / sampleSize
334
+ sampledHeight = (height.toLong() + sampleSize - 1L) / sampleSize
335
+ }
336
+ return sampleSize.toInt().coerceAtLeast(1)
337
+ }
338
+
339
+ private fun exceedsArgbBytes(width: Long, height: Long, maximumBytes: Long): Boolean =
340
+ width <= 0L || height <= 0L || maximumBytes <= 0L ||
341
+ width > Long.MAX_VALUE / height ||
342
+ width * height > maximumBytes / 4L
343
+
344
+ private fun decodeBitmapLease(
345
+ bytes: ByteArray,
346
+ policy: ImageLoadingPolicy,
347
+ priority: DecodedBitmapPriority
348
+ ): DecodedBitmapLease? {
349
+ bitmapDecoderOverride?.let {
350
+ val bitmap = try {
351
+ it(bytes, policy)
352
+ } catch (_: OutOfMemoryError) {
353
+ null
354
+ } ?: return null
355
+ val bytesRetained = decodedAllocationBytes(bitmap)
356
+ val reservation = DecodedBitmapBudget.shared().reserve(
357
+ bytesRetained,
358
+ priority
359
+ ) ?: return constrainUnbudgetedBitmap(bitmap, bytesRetained, policy, priority)
360
+ val lease = reservation.commit(bitmap, bytesRetained)
361
+ ?: return constrainUnbudgetedBitmap(bitmap, bytesRetained, policy, priority)
362
+ return constrainDecodedLease(lease, policy, priority)
363
+ }
364
+ val bounds = BitmapFactory.Options().apply {
365
+ inJustDecodeBounds = true
366
+ }
367
+ BitmapFactory.decodeByteArray(bytes, 0, bytes.size, bounds)
368
+ if (bounds.outWidth <= 0 || bounds.outHeight <= 0) {
369
+ return decodeSvgLease(bytes, policy, priority)
370
+ }
371
+
372
+ val sampleSize = calculateInSampleSize(
373
+ bounds.outWidth,
374
+ bounds.outHeight,
375
+ policy.maxDecodeDimensionPx,
376
+ policy.maxDecodeDimensionPx,
377
+ policy.maxDecodedBytes.toLong()
378
+ )
379
+ val estimatedBytes = estimatedArgbBytes(bounds.outWidth, bounds.outHeight, sampleSize)
380
+ if (estimatedBytes > policy.maxDecodedBytes.toLong()) return null
381
+ val reservation = DecodedBitmapBudget.shared().reserve(
382
+ estimatedBytes,
383
+ priority
384
+ ) ?: return null
385
+ val bitmap = try {
386
+ BitmapFactory.decodeByteArray(
387
+ bytes,
388
+ 0,
389
+ bytes.size,
390
+ BitmapFactory.Options().apply { inSampleSize = sampleSize }
391
+ )
392
+ } catch (_: OutOfMemoryError) {
393
+ null
394
+ } ?: run {
395
+ reservation.close()
396
+ return null
397
+ }
398
+ val decodedBytes = decodedAllocationBytes(bitmap)
399
+ val lease = reservation.commit(bitmap, decodedBytes)
400
+ ?: return constrainUnbudgetedBitmap(bitmap, decodedBytes, policy, priority)
401
+ return constrainDecodedLease(lease, policy, priority)
402
+ }
403
+
404
+ private fun decodeSvgLease(
405
+ bytes: ByteArray,
406
+ policy: ImageLoadingPolicy,
407
+ priority: DecodedBitmapPriority
408
+ ): DecodedBitmapLease? {
409
+ val svg = try {
410
+ if (!isSelfContainedSvg(bytes)) return null
411
+ SVG.getFromInputStream(bytes.inputStream())
412
+ } catch (_: Exception) {
413
+ return null
414
+ } catch (_: OutOfMemoryError) {
415
+ return null
416
+ }
417
+ val viewBox = svg.documentViewBox
418
+ val aspectRatio = svg.documentAspectRatio.toDouble()
419
+ var width = svg.documentWidth.toDouble()
420
+ var height = svg.documentHeight.toDouble()
421
+ if (width <= 0 && height > 0 && aspectRatio > 0) width = height * aspectRatio
422
+ if (height <= 0 && width > 0 && aspectRatio > 0) height = width / aspectRatio
423
+ if (width <= 0) width = viewBox?.width()?.toDouble() ?: 300.0
424
+ if (height <= 0) height = viewBox?.height()?.toDouble() ?: 150.0
425
+ if (!width.isFinite() || !height.isFinite() || width <= 0 || height <= 0 ||
426
+ policy.maxDecodedBytes < 4 || policy.maxDecodeDimensionPx <= 0
427
+ ) {
428
+ return null
429
+ }
430
+ val scale = minOf(
431
+ 1.0,
432
+ policy.maxDecodeDimensionPx / width,
433
+ policy.maxDecodeDimensionPx / height,
434
+ kotlin.math.sqrt(policy.maxDecodedBytes.toDouble() / 4.0 / width / height)
435
+ )
436
+ val targetWidth = kotlin.math.floor(width * scale).toInt().coerceAtLeast(1)
437
+ val targetHeight = kotlin.math.floor(height * scale).toInt().coerceAtLeast(1)
438
+ val estimatedBytes = estimatedArgbBytes(targetWidth, targetHeight, 1)
439
+ if (estimatedBytes > policy.maxDecodedBytes) return null
440
+ val reservation =
441
+ DecodedBitmapBudget.shared().reserve(estimatedBytes, priority) ?: return null
442
+ var bitmap: Bitmap? = null
443
+ try {
444
+ bitmap = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888)
445
+ if (viewBox == null) svg.setDocumentViewBox(0f, 0f, width.toFloat(), height.toFloat())
446
+ svg.setDocumentWidth("100%")
447
+ svg.setDocumentHeight("100%")
448
+ svg.renderToCanvas(Canvas(bitmap))
449
+ val decodedBytes = decodedAllocationBytes(bitmap)
450
+ if (decodedBytes > policy.maxDecodedBytes) return null
451
+ val lease = reservation.commit(bitmap, decodedBytes) ?: return null
452
+ bitmap = null
453
+ return lease
454
+ } catch (_: Exception) {
455
+ return null
456
+ } catch (_: OutOfMemoryError) {
457
+ return null
458
+ } finally {
459
+ bitmap?.recycle()
460
+ reservation.close()
461
+ }
462
+ }
463
+
464
+ private const val MAX_SVG_ELEMENTS = 8192
465
+ private const val MAX_SVG_DEPTH = 128
466
+ private val svgUrlReference = Regex("url\\s*\\(([^)]*)\\)", RegexOption.IGNORE_CASE)
467
+
468
+ private val svgUrlStart = Regex("url\\s*\\(", RegexOption.IGNORE_CASE)
469
+ private val svgLiteralAttributes = setOf("id", "href", "title", "class", "aria-label")
470
+
471
+ private class SvgNode {
472
+ val children = mutableListOf<Int>()
473
+ val references = mutableListOf<String>()
474
+ }
475
+
476
+ private fun isSelfContainedSvg(bytes: ByteArray): Boolean {
477
+ val parser = Xml.newPullParser()
478
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true)
479
+ parser.setInput(bytes.inputStream(), null)
480
+ val nodes = mutableListOf<SvgNode>()
481
+ val ids = mutableMapOf<String, Int>()
482
+ val stack = ArrayDeque<Int>()
483
+ var styleText: StringBuilder? = null
484
+ while (true) {
485
+ when (parser.nextToken()) {
486
+ XmlPullParser.END_DOCUMENT -> break
487
+
488
+ // Reject declarations before AndroidSVG can expand entities or load CSS.
489
+ XmlPullParser.DOCDECL, XmlPullParser.PROCESSING_INSTRUCTION -> return false
490
+
491
+ XmlPullParser.START_TAG -> {
492
+ if (nodes.isEmpty() && (
493
+ parser.name != "svg" ||
494
+ parser.namespace != "http://www.w3.org/2000/svg"
495
+ )
496
+ ) {
497
+ return false
498
+ }
499
+ if (nodes.size >= MAX_SVG_ELEMENTS || stack.size >= MAX_SVG_DEPTH ||
500
+ parser.name in setOf("script", "foreignObject", "image")
501
+ ) {
502
+ return false
503
+ }
504
+ val node = SvgNode()
505
+ val index = nodes.size
506
+ stack.lastOrNull()?.let { nodes[it].children.add(index) }
507
+ nodes.add(node)
508
+ stack.addLast(index)
509
+ if (parser.name == "style") styleText = StringBuilder()
510
+ for (attribute in 0 until parser.attributeCount) {
511
+ val name = parser.getAttributeName(attribute)
512
+ val value = parser.getAttributeValue(attribute)
513
+ val literal = name in svgLiteralAttributes || name.startsWith("data-")
514
+ if (name.startsWith("on", ignoreCase = true) ||
515
+ (name == "href" && !value.trim().startsWith("#")) ||
516
+ (!literal && !hasOnlyLocalSvgReferences(value))
517
+ ) {
518
+ return false
519
+ }
520
+ if (name == "id" && ids.put(value, index) != null) return false
521
+ if (name == "href") node.references.add(value.trim().removePrefix("#"))
522
+ if (!literal) {
523
+ svgUrlReference.findAll(value).forEach {
524
+ node.references.add(
525
+ it.groupValues[1].trim().trim('\'', '"').removePrefix("#")
526
+ )
527
+ }
528
+ }
529
+ }
530
+ }
531
+
532
+ XmlPullParser.TEXT, XmlPullParser.CDSECT, XmlPullParser.ENTITY_REF ->
533
+ styleText?.append(parser.text.orEmpty())
534
+
535
+ XmlPullParser.END_TAG -> {
536
+ if (parser.name == "style") {
537
+ val css = styleText.toString()
538
+ // Stylesheet selectors obscure reference cycles; inline styles remain supported.
539
+ if (!hasOnlyLocalSvgReferences(css) ||
540
+ svgUrlReference.containsMatchIn(css)
541
+ ) {
542
+ return false
543
+ }
544
+ styleText = null
545
+ }
546
+ stack.removeLast()
547
+ }
548
+ }
549
+ }
550
+ if (nodes.isEmpty()) return false
551
+ val costs = IntArray(nodes.size)
552
+ val heights = IntArray(nodes.size)
553
+ val visiting = BooleanArray(nodes.size)
554
+ fun expandedCost(index: Int, depth: Int): Int {
555
+ if (depth > MAX_SVG_DEPTH || visiting[index]) return MAX_SVG_ELEMENTS + 1
556
+ if (costs[index] != 0) {
557
+ return if (depth + heights[index] - 1 <= MAX_SVG_DEPTH) {
558
+ costs[index]
559
+ } else {
560
+ MAX_SVG_ELEMENTS + 1
561
+ }
562
+ }
563
+ visiting[index] = true
564
+ var cost = 1
565
+ var height = 1
566
+ val node = nodes[index]
567
+ for (child in node.children + node.references.mapNotNull { ids[it] }) {
568
+ cost += expandedCost(child, depth + 1)
569
+ height = maxOf(height, 1 + heights[child])
570
+ if (cost > MAX_SVG_ELEMENTS) break
571
+ }
572
+ visiting[index] = false
573
+ costs[index] = cost
574
+ heights[index] = height
575
+ return cost
576
+ }
577
+ return expandedCost(0, 1) <= MAX_SVG_ELEMENTS
578
+ }
579
+
580
+ private fun hasOnlyLocalSvgReferences(value: String): Boolean {
581
+ if (value.contains('\\') || value.contains('@')) return false
582
+ if (!svgUrlReference.findAll(value).all {
583
+ it.groupValues[1].trim().trim('\'', '"').startsWith("#")
584
+ }
585
+ ) {
586
+ return false
587
+ }
588
+ return !svgUrlStart.containsMatchIn(svgUrlReference.replace(value, ""))
589
+ }
590
+
591
+ private data class ConstrainedBitmapTarget(
592
+ val width: Int,
593
+ val height: Int,
594
+ val estimatedBytes: Long
595
+ )
596
+
597
+ private fun constrainedBitmapTarget(
598
+ bitmap: Bitmap,
599
+ decodedBytes: Long,
600
+ policy: ImageLoadingPolicy
601
+ ): ConstrainedBitmapTarget? {
602
+ val maximumDimension = policy.maxDecodeDimensionPx
603
+ val dimensionScale = minOf(
604
+ 1.0,
605
+ maximumDimension.toDouble() / bitmap.width.toDouble(),
606
+ maximumDimension.toDouble() / bitmap.height.toDouble()
607
+ )
608
+ val byteScale = if (decodedBytes > policy.maxDecodedBytes.toLong()) {
609
+ kotlin.math.sqrt(policy.maxDecodedBytes.toDouble() / decodedBytes.toDouble())
610
+ } else {
611
+ 1.0
612
+ }
613
+ val scale = minOf(dimensionScale, byteScale)
614
+ if (scale >= 1.0) return null
615
+ val targetWidth = kotlin.math.floor(bitmap.width.toDouble() * scale)
616
+ .toInt()
617
+ .coerceIn(1, maximumDimension)
618
+ val targetHeight = kotlin.math.floor(bitmap.height.toDouble() * scale)
619
+ .toInt()
620
+ .coerceIn(1, maximumDimension)
621
+ return ConstrainedBitmapTarget(
622
+ targetWidth,
623
+ targetHeight,
624
+ estimatedArgbBytes(targetWidth, targetHeight, 1)
625
+ )
626
+ }
627
+
628
+ private fun constrainUnbudgetedBitmap(
629
+ bitmap: Bitmap,
630
+ decodedBytes: Long,
631
+ policy: ImageLoadingPolicy,
632
+ priority: DecodedBitmapPriority
633
+ ): DecodedBitmapLease? {
634
+ val target = constrainedBitmapTarget(bitmap, decodedBytes, policy) ?: return null
635
+ val reservation = DecodedBitmapBudget.shared().reserve(
636
+ target.estimatedBytes,
637
+ priority
638
+ ) ?: return null
639
+ val constrained = try {
640
+ Bitmap.createScaledBitmap(bitmap, target.width, target.height, true)
641
+ } catch (_: RuntimeException) {
642
+ null
643
+ } catch (_: OutOfMemoryError) {
644
+ null
645
+ } ?: run {
646
+ reservation.close()
647
+ return null
648
+ }
649
+ if (constrained === bitmap) {
650
+ reservation.close()
651
+ return null
652
+ }
653
+ val lease =
654
+ reservation.commit(constrained, decodedAllocationBytes(constrained)) ?: return null
655
+ if (lease.byteCount > policy.maxDecodedBytes.toLong()) {
656
+ lease.close()
657
+ return null
658
+ }
659
+ return lease
660
+ }
661
+
662
+ private fun constrainDecodedLease(
663
+ lease: DecodedBitmapLease,
664
+ policy: ImageLoadingPolicy,
665
+ priority: DecodedBitmapPriority = DecodedBitmapPriority.VISIBLE
666
+ ): DecodedBitmapLease? {
667
+ val bitmap = lease.bitmap
668
+ val decodedBytes = lease.byteCount
669
+ val target = constrainedBitmapTarget(bitmap, decodedBytes, policy) ?: return lease
670
+ val reservation = DecodedBitmapBudget.shared().reserve(
671
+ target.estimatedBytes,
672
+ priority
673
+ ) ?: run {
674
+ lease.close()
675
+ return constrainUnbudgetedBitmap(bitmap, decodedBytes, policy, priority)
676
+ }
677
+ val constrained = try {
678
+ Bitmap.createScaledBitmap(bitmap, target.width, target.height, true)
679
+ } catch (_: RuntimeException) {
680
+ null
681
+ } catch (_: OutOfMemoryError) {
682
+ null
683
+ } ?: run {
684
+ reservation.close()
685
+ lease.close()
686
+ return null
687
+ }
688
+ if (constrained === bitmap) {
689
+ reservation.close()
690
+ return if (lease.byteCount <= policy.maxDecodedBytes.toLong()) {
691
+ lease
692
+ } else {
693
+ lease.close()
694
+ null
695
+ }
696
+ }
697
+ val constrainedLease = reservation.commit(constrained, decodedAllocationBytes(constrained))
698
+ lease.close()
699
+ constrainedLease ?: return null
700
+ if (constrainedLease.byteCount > policy.maxDecodedBytes.toLong()) {
701
+ constrainedLease.close()
702
+ return null
703
+ }
704
+ return constrainedLease
705
+ }
706
+
707
+ private fun estimatedArgbBytes(width: Int, height: Int, sampleSize: Int): Long {
708
+ if (width <= 0 || height <= 0 || sampleSize <= 0) return Long.MAX_VALUE
709
+ val sampledWidth = (width.toLong() + sampleSize - 1L) / sampleSize
710
+ val sampledHeight = (height.toLong() + sampleSize - 1L) / sampleSize
711
+ if (sampledWidth > Long.MAX_VALUE / sampledHeight) return Long.MAX_VALUE
712
+ val pixels = sampledWidth * sampledHeight
713
+ return if (pixels > Long.MAX_VALUE / 4L) Long.MAX_VALUE else pixels * 4L
714
+ }
715
+
716
+ private fun decodedAllocationBytes(bitmap: Bitmap): Long {
717
+ val allocation = runCatching { bitmap.allocationByteCount.toLong() }.getOrNull()
718
+ if (allocation != null && allocation > 0L) return allocation
719
+ return estimatedArgbBytes(bitmap.width, bitmap.height, 1)
720
+ }
721
+
722
+ internal fun resetForTesting() {
723
+ connectionFactoryOverride = null
724
+ bitmapDecoderOverride = null
725
+ }
726
+
727
+ private fun sourceSummary(source: String): String {
728
+ if (!source.regionMatches(0, "data:image/", 0, "data:image/".length, ignoreCase = true)) {
729
+ return "urlLength=${source.length}"
730
+ }
731
+ var commaIndex = -1
732
+ for (index in 0..minOf(source.lastIndex, MAX_DATA_URL_METADATA_BYTES)) {
733
+ if (source[index] == ',') {
734
+ commaIndex = index
735
+ break
736
+ }
737
+ }
738
+ if (commaIndex <= 0) {
739
+ return "dataUrlLength=${source.length}"
740
+ }
741
+ val metadata = source.substring(0, commaIndex)
742
+ val payloadLength = source.length - commaIndex - 1
743
+ return "$metadata payloadLength=$payloadLength"
744
+ }
745
+
746
+ private const val MAX_DATA_URL_METADATA_BYTES = 256
747
+ private const val DATA_URL_WHITESPACE_ALLOWANCE_BYTES = 4_096L
748
+ }