@mathers101/react-native-render-rich-text 0.0.0-stage → 0.1.10
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.
- package/LICENSE +20 -0
- package/README.md +140 -3
- package/ReactNativeRenderRichText.podspec +23 -0
- package/android/build.gradle +86 -0
- package/android/consumer-rules.pro +3 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/jni-adapter.cpp +191 -0
- package/android/src/main/java/com/renderrichtext/EnrichedTiptapText.kt +673 -0
- package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextLayoutManager.kt +24 -0
- package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextManager.kt +405 -0
- package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextPackage.kt +13 -0
- package/android/src/main/java/com/renderrichtext/MeasurementStore.kt +644 -0
- package/android/src/main/java/com/renderrichtext/accessibility/AccessibilityLabels.kt +17 -0
- package/android/src/main/java/com/renderrichtext/accessibility/AccessibleTiptapTextView.kt +112 -0
- package/android/src/main/java/com/renderrichtext/accessibility/TiptapAccessibilityHelper.kt +576 -0
- package/android/src/main/java/com/renderrichtext/events/CodeBlockPressEvent.kt +24 -0
- package/android/src/main/java/com/renderrichtext/events/ContextMenuItemPressEvent.kt +28 -0
- package/android/src/main/java/com/renderrichtext/events/CopyPressEvent.kt +24 -0
- package/android/src/main/java/com/renderrichtext/events/FactoidPressEvent.kt +23 -0
- package/android/src/main/java/com/renderrichtext/events/ImagePressEvent.kt +25 -0
- package/android/src/main/java/com/renderrichtext/events/LatexErrorEvent.kt +29 -0
- package/android/src/main/java/com/renderrichtext/events/LinkLongPressEvent.kt +23 -0
- package/android/src/main/java/com/renderrichtext/events/LinkPressEvent.kt +23 -0
- package/android/src/main/java/com/renderrichtext/events/MentionLongPressEvent.kt +25 -0
- package/android/src/main/java/com/renderrichtext/events/MentionPressEvent.kt +25 -0
- package/android/src/main/java/com/renderrichtext/events/TaskListItemPressEvent.kt +26 -0
- package/android/src/main/java/com/renderrichtext/math/LatexErrorReporter.kt +9 -0
- package/android/src/main/java/com/renderrichtext/parser/Parser.kt +59 -0
- package/android/src/main/java/com/renderrichtext/parser/TextLinkMatching.kt +35 -0
- package/android/src/main/java/com/renderrichtext/parser/TiptapASTNode.kt +69 -0
- package/android/src/main/java/com/renderrichtext/renderer/BaselineShiftRenderer.kt +45 -0
- package/android/src/main/java/com/renderrichtext/renderer/BlankLineRenderer.kt +39 -0
- package/android/src/main/java/com/renderrichtext/renderer/BlockStyleContext.kt +181 -0
- package/android/src/main/java/com/renderrichtext/renderer/BlockquoteRenderer.kt +243 -0
- package/android/src/main/java/com/renderrichtext/renderer/BlockquoteTextRenderer.kt +56 -0
- package/android/src/main/java/com/renderrichtext/renderer/CodeBlockRenderer.kt +135 -0
- package/android/src/main/java/com/renderrichtext/renderer/CodeRenderer.kt +37 -0
- package/android/src/main/java/com/renderrichtext/renderer/EmphasisRenderer.kt +28 -0
- package/android/src/main/java/com/renderrichtext/renderer/FactoidRenderer.kt +97 -0
- package/android/src/main/java/com/renderrichtext/renderer/HeadingRenderer.kt +81 -0
- package/android/src/main/java/com/renderrichtext/renderer/HighlightRenderer.kt +26 -0
- package/android/src/main/java/com/renderrichtext/renderer/LineBreakRenderer.kt +17 -0
- package/android/src/main/java/com/renderrichtext/renderer/LinkRenderer.kt +90 -0
- package/android/src/main/java/com/renderrichtext/renderer/ListContextManager.kt +109 -0
- package/android/src/main/java/com/renderrichtext/renderer/ListItemRenderer.kt +211 -0
- package/android/src/main/java/com/renderrichtext/renderer/ListRenderer.kt +133 -0
- package/android/src/main/java/com/renderrichtext/renderer/MentionRenderer.kt +85 -0
- package/android/src/main/java/com/renderrichtext/renderer/NodeRenderer.kt +207 -0
- package/android/src/main/java/com/renderrichtext/renderer/ParagraphRenderer.kt +91 -0
- package/android/src/main/java/com/renderrichtext/renderer/Renderer.kt +148 -0
- package/android/src/main/java/com/renderrichtext/renderer/SoftBreakRenderer.kt +17 -0
- package/android/src/main/java/com/renderrichtext/renderer/SpanStyleCache.kt +143 -0
- package/android/src/main/java/com/renderrichtext/renderer/StrikethroughRenderer.kt +28 -0
- package/android/src/main/java/com/renderrichtext/renderer/StrongRenderer.kt +28 -0
- package/android/src/main/java/com/renderrichtext/renderer/TextRenderer.kt +31 -0
- package/android/src/main/java/com/renderrichtext/renderer/ThematicBreakRenderer.kt +46 -0
- package/android/src/main/java/com/renderrichtext/renderer/UnderlineRenderer.kt +28 -0
- package/android/src/main/java/com/renderrichtext/spans/BaseListSpan.kt +164 -0
- package/android/src/main/java/com/renderrichtext/spans/BaselineShiftSpan.kt +27 -0
- package/android/src/main/java/com/renderrichtext/spans/BlockquoteSpan.kt +302 -0
- package/android/src/main/java/com/renderrichtext/spans/CodeBackgroundSpan.kt +205 -0
- package/android/src/main/java/com/renderrichtext/spans/CodeBlockSpan.kt +216 -0
- package/android/src/main/java/com/renderrichtext/spans/CodeSpan.kt +32 -0
- package/android/src/main/java/com/renderrichtext/spans/EmphasisSpan.kt +42 -0
- package/android/src/main/java/com/renderrichtext/spans/FactoidBackgroundSpan.kt +265 -0
- package/android/src/main/java/com/renderrichtext/spans/FactoidPaddingSpan.kt +258 -0
- package/android/src/main/java/com/renderrichtext/spans/FactoidSpan.kt +32 -0
- package/android/src/main/java/com/renderrichtext/spans/FadeInSpan.kt +29 -0
- package/android/src/main/java/com/renderrichtext/spans/HeadingSpan.kt +38 -0
- package/android/src/main/java/com/renderrichtext/spans/HighlightSpan.kt +80 -0
- package/android/src/main/java/com/renderrichtext/spans/ImageSpan.kt +481 -0
- package/android/src/main/java/com/renderrichtext/spans/InlineBackgroundGeometry.kt +42 -0
- package/android/src/main/java/com/renderrichtext/spans/LineHeightSpan.kt +39 -0
- package/android/src/main/java/com/renderrichtext/spans/LinkPillIconCache.kt +154 -0
- package/android/src/main/java/com/renderrichtext/spans/LinkPillSpan.kt +381 -0
- package/android/src/main/java/com/renderrichtext/spans/LinkSpan.kt +68 -0
- package/android/src/main/java/com/renderrichtext/spans/ListItemSpacingSpan.kt +33 -0
- package/android/src/main/java/com/renderrichtext/spans/ListMarkerAnchorSpan.kt +38 -0
- package/android/src/main/java/com/renderrichtext/spans/MarginBottomSpan.kt +76 -0
- package/android/src/main/java/com/renderrichtext/spans/MentionChipSpan.kt +132 -0
- package/android/src/main/java/com/renderrichtext/spans/MentionSpan.kt +46 -0
- package/android/src/main/java/com/renderrichtext/spans/OrderedListSpan.kt +127 -0
- package/android/src/main/java/com/renderrichtext/spans/SpoilerSpan.kt +33 -0
- package/android/src/main/java/com/renderrichtext/spans/StrikethroughSpan.kt +12 -0
- package/android/src/main/java/com/renderrichtext/spans/StrongSpan.kt +39 -0
- package/android/src/main/java/com/renderrichtext/spans/TaskListSpan.kt +143 -0
- package/android/src/main/java/com/renderrichtext/spans/TextSpan.kt +26 -0
- package/android/src/main/java/com/renderrichtext/spans/ThematicBreakSpan.kt +69 -0
- package/android/src/main/java/com/renderrichtext/spans/UnorderedListSpan.kt +96 -0
- package/android/src/main/java/com/renderrichtext/spoiler/SpoilerStubs.kt +46 -0
- package/android/src/main/java/com/renderrichtext/styles/BaseBlockStyle.kt +11 -0
- package/android/src/main/java/com/renderrichtext/styles/BlockquoteStyle.kt +83 -0
- package/android/src/main/java/com/renderrichtext/styles/CodeBlockStyle.kt +80 -0
- package/android/src/main/java/com/renderrichtext/styles/CodeStyle.kt +26 -0
- package/android/src/main/java/com/renderrichtext/styles/EmphasisStyle.kt +21 -0
- package/android/src/main/java/com/renderrichtext/styles/FactoidStyle.kt +21 -0
- package/android/src/main/java/com/renderrichtext/styles/HeadingStyle.kt +33 -0
- package/android/src/main/java/com/renderrichtext/styles/HighlightStyle.kt +19 -0
- package/android/src/main/java/com/renderrichtext/styles/ImageStyle.kt +29 -0
- package/android/src/main/java/com/renderrichtext/styles/InlineImageStyle.kt +17 -0
- package/android/src/main/java/com/renderrichtext/styles/InlineMathStyle.kt +17 -0
- package/android/src/main/java/com/renderrichtext/styles/LinkPillContent.kt +36 -0
- package/android/src/main/java/com/renderrichtext/styles/LinkPillStyle.kt +44 -0
- package/android/src/main/java/com/renderrichtext/styles/LinkStyle.kt +23 -0
- package/android/src/main/java/com/renderrichtext/styles/LinkVariantEntry.kt +34 -0
- package/android/src/main/java/com/renderrichtext/styles/ListStyle.kt +65 -0
- package/android/src/main/java/com/renderrichtext/styles/MathStyle.kt +29 -0
- package/android/src/main/java/com/renderrichtext/styles/MentionStyle.kt +19 -0
- package/android/src/main/java/com/renderrichtext/styles/ParagraphStyle.kt +33 -0
- package/android/src/main/java/com/renderrichtext/styles/SpoilerStyle.kt +27 -0
- package/android/src/main/java/com/renderrichtext/styles/StrikethroughStyle.kt +17 -0
- package/android/src/main/java/com/renderrichtext/styles/StrongStyle.kt +21 -0
- package/android/src/main/java/com/renderrichtext/styles/StyleConfig.kt +411 -0
- package/android/src/main/java/com/renderrichtext/styles/StyleParser.kt +92 -0
- package/android/src/main/java/com/renderrichtext/styles/SubscriptStyle.kt +16 -0
- package/android/src/main/java/com/renderrichtext/styles/SuperscriptStyle.kt +16 -0
- package/android/src/main/java/com/renderrichtext/styles/TableStyle.kt +75 -0
- package/android/src/main/java/com/renderrichtext/styles/TaskListStyle.kt +29 -0
- package/android/src/main/java/com/renderrichtext/styles/TextAlignment.kt +32 -0
- package/android/src/main/java/com/renderrichtext/styles/ThematicBreakStyle.kt +23 -0
- package/android/src/main/java/com/renderrichtext/styles/UnderlineStyle.kt +17 -0
- package/android/src/main/java/com/renderrichtext/styles/VideoStyle.kt +31 -0
- package/android/src/main/java/com/renderrichtext/utils/common/BorderStyleUtils.kt +86 -0
- package/android/src/main/java/com/renderrichtext/utils/common/BreakStrategyUtils.kt +32 -0
- package/android/src/main/java/com/renderrichtext/utils/common/CodeBlockHighlighter.kt +36 -0
- package/android/src/main/java/com/renderrichtext/utils/common/CodeBlockNode.kt +42 -0
- package/android/src/main/java/com/renderrichtext/utils/common/EllipsizeUtils.kt +27 -0
- package/android/src/main/java/com/renderrichtext/utils/common/EnrichedTiptapAncestorUtils.kt +10 -0
- package/android/src/main/java/com/renderrichtext/utils/common/FeatureFlags.kt +9 -0
- package/android/src/main/java/com/renderrichtext/utils/common/LinkRecognitionProps.kt +31 -0
- package/android/src/main/java/com/renderrichtext/utils/common/LinkRegexConfig.kt +56 -0
- package/android/src/main/java/com/renderrichtext/utils/common/MotionPreferences.kt +18 -0
- package/android/src/main/java/com/renderrichtext/utils/common/ReadableMapExtensions.kt +28 -0
- package/android/src/main/java/com/renderrichtext/utils/common/TiptapViewManagerUtils.kt +242 -0
- package/android/src/main/java/com/renderrichtext/utils/common/layout/RTLUtils.kt +6 -0
- package/android/src/main/java/com/renderrichtext/utils/common/serialization/TiptapASTSerializer.kt +368 -0
- package/android/src/main/java/com/renderrichtext/utils/text/ImageCache.kt +88 -0
- package/android/src/main/java/com/renderrichtext/utils/text/ImageDownloader.kt +203 -0
- package/android/src/main/java/com/renderrichtext/utils/text/LocalImageLoader.kt +167 -0
- package/android/src/main/java/com/renderrichtext/utils/text/TailFadeInAnimator.kt +84 -0
- package/android/src/main/java/com/renderrichtext/utils/text/conversion/HTMLGenerator.kt +1105 -0
- package/android/src/main/java/com/renderrichtext/utils/text/conversion/TiptapExtractor.kt +423 -0
- package/android/src/main/java/com/renderrichtext/utils/text/extensions/ParagraphUtils.kt +9 -0
- package/android/src/main/java/com/renderrichtext/utils/text/extensions/SpannableExtensions.kt +9 -0
- package/android/src/main/java/com/renderrichtext/utils/text/extensions/TextPaintExtensions.kt +76 -0
- package/android/src/main/java/com/renderrichtext/utils/text/interaction/CheckboxTouchHelper.kt +61 -0
- package/android/src/main/java/com/renderrichtext/utils/text/interaction/TaskListTapUtils.kt +286 -0
- package/android/src/main/java/com/renderrichtext/utils/text/span/ApplyLineHeight.kt +39 -0
- package/android/src/main/java/com/renderrichtext/utils/text/span/BlockSpacing.kt +142 -0
- package/android/src/main/java/com/renderrichtext/utils/text/span/SpanFlags.kt +43 -0
- package/android/src/main/java/com/renderrichtext/utils/text/span/WidthAwareSpans.kt +30 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/CodeBlockPressHost.kt +15 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/ImagePressHost.kt +19 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/LinkEvents.kt +88 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/LinkLongPressMovementMethod.kt +449 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/SelectionActionMode.kt +235 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/TextSelectionColors.kt +48 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/TextViewSetup.kt +119 -0
- package/android/src/main/java/com/renderrichtext/views/ContextMenuPopup.kt +217 -0
- package/android/src/main/java/com/renderrichtext/views/CopyGlyph.kt +101 -0
- package/android/src/main/java/com/renderrichtext/views/WrapGlyph.kt +40 -0
- package/android/src/main/jni/CMakeLists.txt +52 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/ComponentDescriptors.h +30 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextComponentDescriptor.h +29 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextMeasurementManager.cpp +45 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextMeasurementManager.h +21 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextShadowNode.cpp +38 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextShadowNode.h +53 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextState.cpp +9 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextState.h +24 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/conversions.h +40 -0
- package/cpp/parser/TiptapASTNode.hpp +68 -0
- package/cpp/parser/TiptapParser.cpp +233 -0
- package/cpp/parser/TiptapParser.hpp +13 -0
- package/cpp/vendor/nlohmann/json.hpp +24765 -0
- package/ios/EnrichedTiptapText.h +27 -0
- package/ios/EnrichedTiptapText.mm +1222 -0
- package/ios/attachments/ENRMImageAttachment.h +51 -0
- package/ios/attachments/ENRMImageAttachment.m +398 -0
- package/ios/attachments/ENRMLinkPillAttachment.h +47 -0
- package/ios/attachments/ENRMLinkPillAttachment.m +324 -0
- package/ios/attachments/ENRMLinkPillIconCache.h +28 -0
- package/ios/attachments/ENRMLinkPillIconCache.m +181 -0
- package/ios/attachments/ENRMLinkPillText.h +27 -0
- package/ios/attachments/ENRMLinkPillText.m +118 -0
- package/ios/attachments/ENRMMathInlineAttachment.h +7 -0
- package/ios/attachments/ENRMMathInlineAttachment.m +4 -0
- package/ios/attachments/ThematicBreakAttachment.h +15 -0
- package/ios/attachments/ThematicBreakAttachment.m +33 -0
- package/ios/internals/ENRMViewFreeMeasurement.h +208 -0
- package/ios/internals/EnrichedTiptapTextComponentDescriptor.h +19 -0
- package/ios/internals/EnrichedTiptapTextShadowNode.h +43 -0
- package/ios/internals/EnrichedTiptapTextShadowNode.mm +69 -0
- package/ios/internals/EnrichedTiptapTextState.h +24 -0
- package/ios/internals/MeasurementCache.h +308 -0
- package/ios/internals/ShadowMeasurementUtils.h +145 -0
- package/ios/parser/ENRMTextLinkAttributes.h +7 -0
- package/ios/parser/ENRMTiptapParser.h +33 -0
- package/ios/parser/ENRMTiptapParser.mm +73 -0
- package/ios/parser/TiptapASTNode.h +57 -0
- package/ios/parser/TiptapASTNode.m +52 -0
- package/ios/parser/TiptapParserBridge.mm +179 -0
- package/ios/renderer/AttributedRenderer.h +23 -0
- package/ios/renderer/AttributedRenderer.m +229 -0
- package/ios/renderer/BaseRenderer.h +33 -0
- package/ios/renderer/BaseRenderer.m +46 -0
- package/ios/renderer/BaselineShiftTextAttributes.h +10 -0
- package/ios/renderer/BaselineShiftTextAttributes.m +26 -0
- package/ios/renderer/BlankLineRenderer.h +4 -0
- package/ios/renderer/BlankLineRenderer.m +51 -0
- package/ios/renderer/BlockquoteRenderer.h +6 -0
- package/ios/renderer/BlockquoteRenderer.m +258 -0
- package/ios/renderer/CodeBlockRenderer.h +9 -0
- package/ios/renderer/CodeBlockRenderer.m +99 -0
- package/ios/renderer/CodeRenderer.h +9 -0
- package/ios/renderer/CodeRenderer.m +69 -0
- package/ios/renderer/ENRMBlockquoteTextRenderer.h +25 -0
- package/ios/renderer/ENRMBlockquoteTextRenderer.m +48 -0
- package/ios/renderer/EmphasisRenderer.h +5 -0
- package/ios/renderer/EmphasisRenderer.m +100 -0
- package/ios/renderer/FactoidRenderer.h +9 -0
- package/ios/renderer/FactoidRenderer.m +130 -0
- package/ios/renderer/HeadingRenderer.h +6 -0
- package/ios/renderer/HeadingRenderer.m +103 -0
- package/ios/renderer/HighlightRenderer.h +7 -0
- package/ios/renderer/HighlightRenderer.m +33 -0
- package/ios/renderer/LinkRenderer.h +6 -0
- package/ios/renderer/LinkRenderer.m +158 -0
- package/ios/renderer/ListItemRenderer.h +24 -0
- package/ios/renderer/ListItemRenderer.m +283 -0
- package/ios/renderer/ListRenderer.h +13 -0
- package/ios/renderer/ListRenderer.m +126 -0
- package/ios/renderer/MentionRenderer.h +9 -0
- package/ios/renderer/MentionRenderer.m +96 -0
- package/ios/renderer/NodeRenderer.h +8 -0
- package/ios/renderer/ParagraphRenderer.h +6 -0
- package/ios/renderer/ParagraphRenderer.m +85 -0
- package/ios/renderer/RenderContext.h +145 -0
- package/ios/renderer/RenderContext.m +403 -0
- package/ios/renderer/RendererFactory.h +12 -0
- package/ios/renderer/RendererFactory.m +158 -0
- package/ios/renderer/StrikethroughRenderer.h +5 -0
- package/ios/renderer/StrikethroughRenderer.m +30 -0
- package/ios/renderer/StrongRenderer.h +5 -0
- package/ios/renderer/StrongRenderer.m +88 -0
- package/ios/renderer/TextRenderer.h +6 -0
- package/ios/renderer/TextRenderer.m +18 -0
- package/ios/renderer/ThematicBreakRenderer.h +4 -0
- package/ios/renderer/ThematicBreakRenderer.m +42 -0
- package/ios/renderer/UnderlineRenderer.h +4 -0
- package/ios/renderer/UnderlineRenderer.m +41 -0
- package/ios/styles/ENRMFontSlot.h +12 -0
- package/ios/styles/ENRMFontSlot.m +24 -0
- package/ios/styles/StyleConfig.h +488 -0
- package/ios/styles/StyleConfig.mm +2820 -0
- package/ios/utils/AccessibilityInfo.h +35 -0
- package/ios/utils/AccessibilityInfo.m +24 -0
- package/ios/utils/BlockquoteBorder.h +26 -0
- package/ios/utils/BlockquoteBorder.m +294 -0
- package/ios/utils/CodeBackground.h +19 -0
- package/ios/utils/CodeBackground.m +197 -0
- package/ios/utils/CodeBlockBackground.h +17 -0
- package/ios/utils/CodeBlockBackground.m +103 -0
- package/ios/utils/ContextMenuUtils.h +76 -0
- package/ios/utils/ContextMenuUtils.mm +75 -0
- package/ios/utils/ENRMAccessibilityLabels.h +40 -0
- package/ios/utils/ENRMAccessibilityLabels.m +5 -0
- package/ios/utils/ENRMAsyncRenderCoordinator.h +24 -0
- package/ios/utils/ENRMAsyncRenderCoordinator.m +37 -0
- package/ios/utils/ENRMAtomicSize.h +65 -0
- package/ios/utils/ENRMCodeBlockContent.h +37 -0
- package/ios/utils/ENRMCodeBlockContent.mm +89 -0
- package/ios/utils/ENRMFeatureFlags.h +25 -0
- package/ios/utils/ENRMImageDownloader.h +30 -0
- package/ios/utils/ENRMImageDownloader.m +168 -0
- package/ios/utils/ENRMLatexErrorCoordinator.h +27 -0
- package/ios/utils/ENRMLatexErrorCoordinator.mm +75 -0
- package/ios/utils/ENRMLatexErrorReporting.h +13 -0
- package/ios/utils/ENRMLinkContextMenus.h +93 -0
- package/ios/utils/ENRMLinkContextMenus.m +178 -0
- package/ios/utils/ENRMLinkRegexConfig.h +30 -0
- package/ios/utils/ENRMLinkRegexConfig.mm +104 -0
- package/ios/utils/ENRMLinkRegexProps.h +23 -0
- package/ios/utils/ENRMLocalImageLoader.h +35 -0
- package/ios/utils/ENRMLocalImageLoader.m +111 -0
- package/ios/utils/ENRMMenuAction.h +27 -0
- package/ios/utils/ENRMMenuAction.m +33 -0
- package/ios/utils/ENRMSpoilerOverlayManager.h +15 -0
- package/ios/utils/ENRMSpoilerOverlayManager.m +19 -0
- package/ios/utils/ENRMSpoilerTapUtils.h +6 -0
- package/ios/utils/ENRMSpoilerTapUtils.m +11 -0
- package/ios/utils/ENRMTailFadeInAnimator.h +16 -0
- package/ios/utils/ENRMTailFadeInAnimator.m +161 -0
- package/ios/utils/ENRMTextHitTest.h +23 -0
- package/ios/utils/ENRMTextHitTest.m +20 -0
- package/ios/utils/ENRMTextInteractionUtils.h +20 -0
- package/ios/utils/ENRMTextInteractionUtils.m +15 -0
- package/ios/utils/ENRMTextRenderer.h +38 -0
- package/ios/utils/ENRMTextRenderer.m +54 -0
- package/ios/utils/ENRMTextViewSetup.h +132 -0
- package/ios/utils/ENRMUIKit.h +429 -0
- package/ios/utils/EditMenuUtils.h +105 -0
- package/ios/utils/EditMenuUtils.m +143 -0
- package/ios/utils/FactoidBackground.h +20 -0
- package/ios/utils/FactoidBackground.m +161 -0
- package/ios/utils/FontScaleObserver.h +10 -0
- package/ios/utils/FontScaleObserver.m +55 -0
- package/ios/utils/FontUtils.h +31 -0
- package/ios/utils/FontUtils.m +63 -0
- package/ios/utils/HTMLGenerator.h +25 -0
- package/ios/utils/HTMLGenerator.m +1041 -0
- package/ios/utils/HeightUpdateUtils.h +25 -0
- package/ios/utils/ImageRequestHeaderUtils.h +35 -0
- package/ios/utils/LastElementUtils.h +97 -0
- package/ios/utils/LinkContextMenuUtils.h +55 -0
- package/ios/utils/LinkPillContentUtils.h +46 -0
- package/ios/utils/LinkTapUtils.h +56 -0
- package/ios/utils/LinkTapUtils.m +139 -0
- package/ios/utils/ListMarkerDrawer.h +15 -0
- package/ios/utils/ListMarkerDrawer.m +239 -0
- package/ios/utils/MentionBackground.h +26 -0
- package/ios/utils/MentionBackground.m +86 -0
- package/ios/utils/ParagraphStyleUtils.h +87 -0
- package/ios/utils/ParagraphStyleUtils.m +484 -0
- package/ios/utils/PasteboardUtils.h +48 -0
- package/ios/utils/PasteboardUtils.m +166 -0
- package/ios/utils/RTFExportUtils.h +24 -0
- package/ios/utils/RTFExportUtils.m +299 -0
- package/ios/utils/RuntimeKeys.h +44 -0
- package/ios/utils/RuntimeKeys.m +13 -0
- package/ios/utils/SelectionColorUtils.h +14 -0
- package/ios/utils/StylePropsUtils.h +1340 -0
- package/ios/utils/TaskListTapUtils.h +6 -0
- package/ios/utils/TaskListTapUtils.m +14 -0
- package/ios/utils/TextViewLayoutManager.h +14 -0
- package/ios/utils/TextViewLayoutManager.mm +198 -0
- package/ios/utils/TiptapAccessibilityElementBuilder.h +43 -0
- package/ios/utils/TiptapAccessibilityElementBuilder.m +432 -0
- package/ios/utils/TiptapExtractor.h +17 -0
- package/ios/utils/TiptapExtractor.m +367 -0
- package/ios/views/ENRMTiptapTextView.h +25 -0
- package/ios/views/ENRMTiptapTextView.m +247 -0
- package/package.json +70 -6
- package/react-native.config.js +10 -0
- package/src/EnrichedTiptapTextNativeComponent.ts +739 -0
- package/src/RichText.tsx +164 -0
- package/src/admonitionDefaults.ts +58 -0
- package/src/darkModeTextStyle.ts +99 -0
- package/src/headingDefaults.ts +21 -0
- package/src/index.tsx +4 -0
- package/src/linkVariantUtils.ts +120 -0
- package/src/normalizeTiptapStyle.ts +397 -0
- package/src/rn-globals.d.ts +1 -0
- package/src/styleUtils.ts +107 -0
- package/src/types/TiptapStyle.ts +532 -0
- package/src/types/TiptapStyleInternal.ts +299 -0
- package/src/types/linkPill.ts +9 -0
- 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
|
+
}
|