react-native-enriched-markdown 0.1.0 → 0.2.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.
- package/LICENSE +20 -0
- package/README.md +551 -0
- package/ReactNativeEnrichedMarkdown.podspec +27 -0
- package/android/build.gradle +101 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +54 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +26 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ComponentDescriptors.cpp +22 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ComponentDescriptors.h +24 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +33 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +31 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +82 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +1388 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ShadowNodes.cpp +17 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ShadowNodes.h +32 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/States.cpp +16 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/States.h +20 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/baseline-prof.txt +65 -0
- package/android/src/main/cpp/jni-adapter.cpp +220 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +270 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextLayoutManager.kt +15 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +173 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextPackage.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/MeasurementStore.kt +385 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/accessibility/MarkdownAccessibilityHelper.kt +279 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/events/LinkLongPressEvent.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/events/LinkPressEvent.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/parser/MarkdownASTNode.kt +31 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/parser/Parser.kt +62 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockStyleContext.kt +166 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockquoteRenderer.kt +84 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeBlockRenderer.kt +104 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeRenderer.kt +36 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/DocumentRenderer.kt +16 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/EmphasisRenderer.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +70 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ImageRenderer.kt +68 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LineBreakRenderer.kt +16 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LinkRenderer.kt +29 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListContextManager.kt +105 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListItemRenderer.kt +59 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt +76 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/NodeRenderer.kt +103 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ParagraphRenderer.kt +80 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/Renderer.kt +109 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +86 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/StrikethroughRenderer.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/StrongRenderer.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/TextRenderer.kt +30 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ThematicBreakRenderer.kt +45 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/UnderlineRenderer.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/BaseListSpan.kt +136 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/BlockquoteSpan.kt +135 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBackgroundSpan.kt +180 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBlockSpan.kt +196 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeSpan.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/EmphasisSpan.kt +34 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/HeadingSpan.kt +38 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/ImageSpan.kt +321 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LineHeightSpan.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkSpan.kt +51 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/MarginBottomSpan.kt +76 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/OrderedListSpan.kt +87 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/StrikethroughSpan.kt +12 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/StrongSpan.kt +37 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/TextSpan.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/ThematicBreakSpan.kt +69 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/UnorderedListSpan.kt +69 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/BaseBlockStyle.kt +11 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/BlockquoteStyle.kt +51 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeBlockStyle.kt +54 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeStyle.kt +21 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/EmphasisStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/HeadingStyle.kt +33 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ImageStyle.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/InlineImageStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/LinkStyle.kt +19 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ListStyle.kt +57 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ParagraphStyle.kt +33 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StrikethroughStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StrongStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleConfig.kt +211 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleParser.kt +92 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/TextAlignment.kt +32 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ThematicBreakStyle.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/UnderlineStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/AsyncDrawable.kt +91 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/HTMLGenerator.kt +827 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/LinkLongPressMovementMethod.kt +121 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/MarkdownExtractor.kt +375 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/SelectionActionMode.kt +139 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/Utils.kt +183 -0
- package/android/src/main/jni/CMakeLists.txt +70 -0
- package/android/src/main/jni/EnrichedMarkdownTextSpec.cpp +21 -0
- package/android/src/main/jni/EnrichedMarkdownTextSpec.h +25 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextComponentDescriptor.h +29 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextMeasurementManager.cpp +45 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextMeasurementManager.h +21 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.cpp +20 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.h +37 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/conversions.h +22 -0
- package/cpp/md4c/md4c.c +6492 -0
- package/cpp/md4c/md4c.h +402 -0
- package/cpp/parser/MD4CParser.cpp +327 -0
- package/cpp/parser/MD4CParser.hpp +27 -0
- package/cpp/parser/MarkdownASTNode.hpp +51 -0
- package/ios/EnrichedMarkdownText.h +18 -0
- package/ios/EnrichedMarkdownText.mm +1401 -0
- package/ios/attachments/EnrichedMarkdownImageAttachment.h +23 -0
- package/ios/attachments/EnrichedMarkdownImageAttachment.m +185 -0
- package/ios/attachments/ThematicBreakAttachment.h +15 -0
- package/ios/attachments/ThematicBreakAttachment.m +33 -0
- package/ios/generated/EnrichedMarkdownTextSpec/ComponentDescriptors.cpp +22 -0
- package/ios/generated/EnrichedMarkdownTextSpec/ComponentDescriptors.h +24 -0
- package/ios/generated/EnrichedMarkdownTextSpec/EventEmitters.cpp +33 -0
- package/ios/generated/EnrichedMarkdownTextSpec/EventEmitters.h +31 -0
- package/ios/generated/EnrichedMarkdownTextSpec/Props.cpp +82 -0
- package/ios/generated/EnrichedMarkdownTextSpec/Props.h +1388 -0
- package/ios/generated/EnrichedMarkdownTextSpec/RCTComponentViewHelpers.h +20 -0
- package/ios/generated/EnrichedMarkdownTextSpec/ShadowNodes.cpp +17 -0
- package/ios/generated/EnrichedMarkdownTextSpec/ShadowNodes.h +32 -0
- package/ios/generated/EnrichedMarkdownTextSpec/States.cpp +16 -0
- package/ios/generated/EnrichedMarkdownTextSpec/States.h +20 -0
- package/ios/internals/EnrichedMarkdownTextComponentDescriptor.h +19 -0
- package/ios/internals/EnrichedMarkdownTextShadowNode.h +43 -0
- package/ios/internals/EnrichedMarkdownTextShadowNode.mm +85 -0
- package/ios/internals/EnrichedMarkdownTextState.h +24 -0
- package/ios/parser/MarkdownASTNode.h +35 -0
- package/ios/parser/MarkdownASTNode.m +32 -0
- package/ios/parser/MarkdownParser.h +17 -0
- package/ios/parser/MarkdownParser.mm +42 -0
- package/ios/parser/MarkdownParserBridge.mm +120 -0
- package/ios/renderer/AttributedRenderer.h +11 -0
- package/ios/renderer/AttributedRenderer.m +152 -0
- package/ios/renderer/BlockquoteRenderer.h +7 -0
- package/ios/renderer/BlockquoteRenderer.m +160 -0
- package/ios/renderer/CodeBlockRenderer.h +10 -0
- package/ios/renderer/CodeBlockRenderer.m +90 -0
- package/ios/renderer/CodeRenderer.h +10 -0
- package/ios/renderer/CodeRenderer.m +60 -0
- package/ios/renderer/EmphasisRenderer.h +6 -0
- package/ios/renderer/EmphasisRenderer.m +96 -0
- package/ios/renderer/HeadingRenderer.h +7 -0
- package/ios/renderer/HeadingRenderer.m +105 -0
- package/ios/renderer/ImageRenderer.h +12 -0
- package/ios/renderer/ImageRenderer.m +83 -0
- package/ios/renderer/LinkRenderer.h +7 -0
- package/ios/renderer/LinkRenderer.m +69 -0
- package/ios/renderer/ListItemRenderer.h +16 -0
- package/ios/renderer/ListItemRenderer.m +103 -0
- package/ios/renderer/ListRenderer.h +13 -0
- package/ios/renderer/ListRenderer.m +70 -0
- package/ios/renderer/NodeRenderer.h +8 -0
- package/ios/renderer/ParagraphRenderer.h +7 -0
- package/ios/renderer/ParagraphRenderer.m +80 -0
- package/ios/renderer/RenderContext.h +105 -0
- package/ios/renderer/RenderContext.m +312 -0
- package/ios/renderer/RendererFactory.h +12 -0
- package/ios/renderer/RendererFactory.m +116 -0
- package/ios/renderer/StrikethroughRenderer.h +6 -0
- package/ios/renderer/StrikethroughRenderer.m +40 -0
- package/ios/renderer/StrongRenderer.h +6 -0
- package/ios/renderer/StrongRenderer.m +83 -0
- package/ios/renderer/TextRenderer.h +6 -0
- package/ios/renderer/TextRenderer.m +16 -0
- package/ios/renderer/ThematicBreakRenderer.h +5 -0
- package/ios/renderer/ThematicBreakRenderer.m +53 -0
- package/ios/renderer/UnderlineRenderer.h +6 -0
- package/ios/renderer/UnderlineRenderer.m +39 -0
- package/ios/styles/StyleConfig.h +274 -0
- package/ios/styles/StyleConfig.mm +1806 -0
- package/ios/utils/AccessibilityInfo.h +35 -0
- package/ios/utils/AccessibilityInfo.m +24 -0
- package/ios/utils/BlockquoteBorder.h +20 -0
- package/ios/utils/BlockquoteBorder.m +92 -0
- package/ios/utils/CodeBackground.h +19 -0
- package/ios/utils/CodeBackground.m +191 -0
- package/ios/utils/CodeBlockBackground.h +17 -0
- package/ios/utils/CodeBlockBackground.m +82 -0
- package/ios/utils/EditMenuUtils.h +22 -0
- package/ios/utils/EditMenuUtils.m +118 -0
- package/ios/utils/FontUtils.h +25 -0
- package/ios/utils/FontUtils.m +27 -0
- package/ios/utils/HTMLGenerator.h +20 -0
- package/ios/utils/HTMLGenerator.m +793 -0
- package/ios/utils/LastElementUtils.h +53 -0
- package/ios/utils/ListMarkerDrawer.h +15 -0
- package/ios/utils/ListMarkerDrawer.m +127 -0
- package/ios/utils/MarkdownAccessibilityElementBuilder.h +45 -0
- package/ios/utils/MarkdownAccessibilityElementBuilder.m +323 -0
- package/ios/utils/MarkdownExtractor.h +17 -0
- package/ios/utils/MarkdownExtractor.m +308 -0
- package/ios/utils/ParagraphStyleUtils.h +21 -0
- package/ios/utils/ParagraphStyleUtils.m +111 -0
- package/ios/utils/PasteboardUtils.h +36 -0
- package/ios/utils/PasteboardUtils.m +134 -0
- package/ios/utils/RTFExportUtils.h +24 -0
- package/ios/utils/RTFExportUtils.m +297 -0
- package/ios/utils/RuntimeKeys.h +38 -0
- package/ios/utils/RuntimeKeys.m +11 -0
- package/ios/utils/TextViewLayoutManager.h +14 -0
- package/ios/utils/TextViewLayoutManager.mm +113 -0
- package/lib/module/EnrichedMarkdownText.js +65 -0
- package/lib/module/EnrichedMarkdownText.js.map +1 -0
- package/lib/module/EnrichedMarkdownTextNativeComponent.ts +210 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/normalizeMarkdownStyle.js +384 -0
- package/lib/module/normalizeMarkdownStyle.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/EnrichedMarkdownText.d.ts +183 -0
- package/lib/typescript/src/EnrichedMarkdownText.d.ts.map +1 -0
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +185 -0
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +4 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/normalizeMarkdownStyle.d.ts +6 -0
- package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -0
- package/package.json +186 -1
- package/react-native.config.js +13 -0
- package/src/EnrichedMarkdownText.tsx +280 -0
- package/src/EnrichedMarkdownTextNativeComponent.ts +210 -0
- package/src/index.tsx +10 -0
- package/src/normalizeMarkdownStyle.ts +423 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown.spans
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.graphics.Canvas
|
|
6
|
+
import android.graphics.Color
|
|
7
|
+
import android.graphics.Paint
|
|
8
|
+
import android.graphics.Typeface
|
|
9
|
+
import android.text.Layout
|
|
10
|
+
import android.text.Spanned
|
|
11
|
+
import android.text.TextPaint
|
|
12
|
+
import android.text.style.LeadingMarginSpan
|
|
13
|
+
import android.text.style.MetricAffectingSpan
|
|
14
|
+
import com.swmansion.enriched.markdown.renderer.BlockStyle
|
|
15
|
+
import com.swmansion.enriched.markdown.renderer.SpanStyleCache
|
|
16
|
+
import com.swmansion.enriched.markdown.styles.BlockquoteStyle
|
|
17
|
+
import com.swmansion.enriched.markdown.utils.applyBlockStyleFont
|
|
18
|
+
import com.swmansion.enriched.markdown.utils.applyColorPreserving
|
|
19
|
+
|
|
20
|
+
class BlockquoteSpan(
|
|
21
|
+
private val blockquoteStyle: BlockquoteStyle,
|
|
22
|
+
val depth: Int,
|
|
23
|
+
private val context: Context,
|
|
24
|
+
private val styleCache: SpanStyleCache,
|
|
25
|
+
) : MetricAffectingSpan(),
|
|
26
|
+
LeadingMarginSpan {
|
|
27
|
+
private val levelSpacing: Float = blockquoteStyle.borderWidth + blockquoteStyle.gapWidth
|
|
28
|
+
private val blockStyle =
|
|
29
|
+
BlockStyle(
|
|
30
|
+
fontSize = blockquoteStyle.fontSize,
|
|
31
|
+
fontFamily = blockquoteStyle.fontFamily,
|
|
32
|
+
fontWeight = blockquoteStyle.fontWeight,
|
|
33
|
+
color = blockquoteStyle.color,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
// Cache for shouldSkipDrawing to avoid repeated getSpans() calls during draw passes
|
|
37
|
+
private var cachedText: CharSequence? = null
|
|
38
|
+
private var cachedMaxDepthByPosition = mutableMapOf<Int, Int>()
|
|
39
|
+
|
|
40
|
+
override fun updateMeasureState(tp: TextPaint) = applyTextStyle(tp)
|
|
41
|
+
|
|
42
|
+
override fun updateDrawState(tp: TextPaint) = applyTextStyle(tp)
|
|
43
|
+
|
|
44
|
+
override fun getLeadingMargin(first: Boolean): Int = levelSpacing.toInt()
|
|
45
|
+
|
|
46
|
+
override fun drawLeadingMargin(
|
|
47
|
+
c: Canvas,
|
|
48
|
+
p: Paint,
|
|
49
|
+
x: Int,
|
|
50
|
+
dir: Int,
|
|
51
|
+
top: Int,
|
|
52
|
+
baseline: Int,
|
|
53
|
+
bottom: Int,
|
|
54
|
+
text: CharSequence?,
|
|
55
|
+
start: Int,
|
|
56
|
+
end: Int,
|
|
57
|
+
first: Boolean,
|
|
58
|
+
layout: Layout?,
|
|
59
|
+
) {
|
|
60
|
+
// Essential check from original: only the deepest span draws to prevent over-rendering background
|
|
61
|
+
if (shouldSkipDrawing(text, start)) return
|
|
62
|
+
|
|
63
|
+
drawBackground(c, top, bottom, layout)
|
|
64
|
+
|
|
65
|
+
val borderPaint = configureBorderPaint()
|
|
66
|
+
val borderTop = top.toFloat()
|
|
67
|
+
val borderBottom = bottom.toFloat()
|
|
68
|
+
val containerLeft = layout?.getLineLeft(0) ?: 0f
|
|
69
|
+
|
|
70
|
+
for (level in 0..depth) {
|
|
71
|
+
val borderX = containerLeft + (levelSpacing * level * dir)
|
|
72
|
+
val borderRight = borderX + (blockquoteStyle.borderWidth * dir)
|
|
73
|
+
c.drawRect(borderX, borderTop, borderRight, borderBottom, borderPaint)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@SuppressLint("WrongConstant") // Result of mask is always valid: 0, 1, 2, or 3
|
|
78
|
+
private fun applyTextStyle(tp: TextPaint) {
|
|
79
|
+
tp.textSize = blockStyle.fontSize
|
|
80
|
+
val preserved = (tp.typeface?.style ?: 0) and BOLD_ITALIC_MASK
|
|
81
|
+
tp.applyBlockStyleFont(blockStyle, context)
|
|
82
|
+
if (preserved != 0) {
|
|
83
|
+
tp.typeface = Typeface.create(tp.typeface ?: Typeface.DEFAULT, preserved)
|
|
84
|
+
}
|
|
85
|
+
tp.applyColorPreserving(blockStyle.color, *styleCache.colorsToPreserve)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
companion object {
|
|
89
|
+
private const val BOLD_ITALIC_MASK = Typeface.BOLD or Typeface.ITALIC
|
|
90
|
+
|
|
91
|
+
private val sharedBorderPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL }
|
|
92
|
+
private val sharedBackgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private fun configureBorderPaint(): Paint =
|
|
96
|
+
sharedBorderPaint.apply {
|
|
97
|
+
color = blockquoteStyle.borderColor
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private fun configureBackgroundPaint(bgColor: Int): Paint =
|
|
101
|
+
sharedBackgroundPaint.apply {
|
|
102
|
+
color = bgColor
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private fun shouldSkipDrawing(
|
|
106
|
+
text: CharSequence?,
|
|
107
|
+
start: Int,
|
|
108
|
+
): Boolean {
|
|
109
|
+
if (text !is Spanned) return false
|
|
110
|
+
|
|
111
|
+
if (cachedText !== text) {
|
|
112
|
+
cachedText = text
|
|
113
|
+
cachedMaxDepthByPosition.clear()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
val maxDepth =
|
|
117
|
+
cachedMaxDepthByPosition.getOrPut(start) {
|
|
118
|
+
val spans = text.getSpans(start, start + 1, BlockquoteSpan::class.java)
|
|
119
|
+
spans.maxOfOrNull { it.depth } ?: -1
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return maxDepth > depth
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private fun drawBackground(
|
|
126
|
+
c: Canvas,
|
|
127
|
+
top: Int,
|
|
128
|
+
bottom: Int,
|
|
129
|
+
layout: Layout?,
|
|
130
|
+
) {
|
|
131
|
+
val bgColor = blockquoteStyle.backgroundColor?.takeIf { it != Color.TRANSPARENT } ?: return
|
|
132
|
+
val backgroundPaint = configureBackgroundPaint(bgColor)
|
|
133
|
+
c.drawRect(0f, top.toFloat(), layout?.width?.toFloat() ?: 0f, bottom.toFloat(), backgroundPaint)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown.spans
|
|
2
|
+
|
|
3
|
+
import android.graphics.Canvas
|
|
4
|
+
import android.graphics.Paint
|
|
5
|
+
import android.graphics.Path
|
|
6
|
+
import android.graphics.RectF
|
|
7
|
+
import android.text.Spanned
|
|
8
|
+
import android.text.StaticLayout
|
|
9
|
+
import android.text.TextPaint
|
|
10
|
+
import android.text.style.LineBackgroundSpan
|
|
11
|
+
import com.swmansion.enriched.markdown.styles.StyleConfig
|
|
12
|
+
import kotlin.math.max
|
|
13
|
+
import kotlin.math.min
|
|
14
|
+
|
|
15
|
+
class CodeBackgroundSpan(
|
|
16
|
+
private val styleConfig: StyleConfig,
|
|
17
|
+
) : LineBackgroundSpan {
|
|
18
|
+
companion object {
|
|
19
|
+
private const val CORNER_RADIUS = 6.0f
|
|
20
|
+
private const val BORDER_WIDTH = 1.0f
|
|
21
|
+
|
|
22
|
+
private val sharedBackgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL }
|
|
23
|
+
private val sharedBorderPaint =
|
|
24
|
+
Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
|
25
|
+
style = Paint.Style.STROKE
|
|
26
|
+
strokeWidth = BORDER_WIDTH
|
|
27
|
+
strokeJoin = Paint.Join.ROUND
|
|
28
|
+
strokeCap = Paint.Cap.ROUND
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Reusable drawing objects per instance
|
|
33
|
+
private val rect = RectF()
|
|
34
|
+
private val path = Path()
|
|
35
|
+
|
|
36
|
+
override fun drawBackground(
|
|
37
|
+
canvas: Canvas,
|
|
38
|
+
p: Paint,
|
|
39
|
+
left: Int,
|
|
40
|
+
right: Int,
|
|
41
|
+
top: Int,
|
|
42
|
+
baseline: Int,
|
|
43
|
+
bottom: Int,
|
|
44
|
+
text: CharSequence,
|
|
45
|
+
start: Int,
|
|
46
|
+
end: Int,
|
|
47
|
+
lineNum: Int,
|
|
48
|
+
) {
|
|
49
|
+
if (text !is Spanned) return
|
|
50
|
+
|
|
51
|
+
val spanStart = text.getSpanStart(this)
|
|
52
|
+
val spanEnd = text.getSpanEnd(this)
|
|
53
|
+
if (spanStart !in 0 until spanEnd) return
|
|
54
|
+
|
|
55
|
+
// 1. Determine relative positioning
|
|
56
|
+
val isFirst = spanStart >= start
|
|
57
|
+
val isLast = spanEnd <= end
|
|
58
|
+
|
|
59
|
+
// 2. Calculate coordinates
|
|
60
|
+
val finalBottom = adjustBottomForMargin(text, end, bottom)
|
|
61
|
+
val startX = if (isFirst) getHorizontalOffset(text, start, end, spanStart, p) + left else left.toFloat()
|
|
62
|
+
val endX = if (isLast) getHorizontalOffset(text, start, end, spanEnd, p) + left else right.toFloat()
|
|
63
|
+
|
|
64
|
+
rect.set(min(startX, endX), top.toFloat(), max(startX, endX), finalBottom.toFloat())
|
|
65
|
+
|
|
66
|
+
// 3. Apply Style
|
|
67
|
+
val codeStyle = styleConfig.codeStyle
|
|
68
|
+
sharedBackgroundPaint.color = codeStyle.backgroundColor
|
|
69
|
+
sharedBorderPaint.color = codeStyle.borderColor
|
|
70
|
+
|
|
71
|
+
drawShapes(canvas, isFirst, isLast)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private fun getHorizontalOffset(
|
|
75
|
+
text: CharSequence,
|
|
76
|
+
lineStart: Int,
|
|
77
|
+
lineEnd: Int,
|
|
78
|
+
index: Int,
|
|
79
|
+
paint: Paint,
|
|
80
|
+
): Float {
|
|
81
|
+
if (index <= lineStart) return 0f
|
|
82
|
+
val lineText = text.subSequence(lineStart, lineEnd)
|
|
83
|
+
val textPaint = paint as? TextPaint ?: TextPaint(paint)
|
|
84
|
+
val layout = StaticLayout.Builder.obtain(lineText, 0, lineText.length, textPaint, 10000).build()
|
|
85
|
+
return layout.getPrimaryHorizontal(index - lineStart)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private fun drawShapes(
|
|
89
|
+
canvas: Canvas,
|
|
90
|
+
isFirst: Boolean,
|
|
91
|
+
isLast: Boolean,
|
|
92
|
+
) {
|
|
93
|
+
val radii = createRadii(isFirst, isLast)
|
|
94
|
+
|
|
95
|
+
path.reset()
|
|
96
|
+
path.addRoundRect(rect, radii, Path.Direction.CW)
|
|
97
|
+
canvas.drawPath(path, sharedBackgroundPaint)
|
|
98
|
+
|
|
99
|
+
if (isFirst && isLast) {
|
|
100
|
+
canvas.drawPath(path, sharedBorderPaint)
|
|
101
|
+
} else {
|
|
102
|
+
drawOpenBorders(canvas, isFirst, isLast)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private fun drawOpenBorders(
|
|
107
|
+
canvas: Canvas,
|
|
108
|
+
isFirst: Boolean,
|
|
109
|
+
isLast: Boolean,
|
|
110
|
+
) {
|
|
111
|
+
val r = CORNER_RADIUS
|
|
112
|
+
path.reset()
|
|
113
|
+
|
|
114
|
+
if (isFirst) {
|
|
115
|
+
path.moveTo(rect.right, rect.top)
|
|
116
|
+
path.lineTo(rect.left + r, rect.top)
|
|
117
|
+
path.quadTo(rect.left, rect.top, rect.left, rect.top + r)
|
|
118
|
+
path.lineTo(rect.left, rect.bottom - r)
|
|
119
|
+
path.quadTo(rect.left, rect.bottom, rect.left + r, rect.bottom)
|
|
120
|
+
path.lineTo(rect.right, rect.bottom)
|
|
121
|
+
} else if (isLast) {
|
|
122
|
+
path.moveTo(rect.left, rect.top)
|
|
123
|
+
path.lineTo(rect.right - r, rect.top)
|
|
124
|
+
path.quadTo(rect.right, rect.top, rect.right, rect.top + r)
|
|
125
|
+
path.lineTo(rect.right, rect.bottom - r)
|
|
126
|
+
path.quadTo(rect.right, rect.bottom, rect.right - r, rect.bottom)
|
|
127
|
+
path.lineTo(rect.left, rect.bottom)
|
|
128
|
+
} else {
|
|
129
|
+
path.moveTo(rect.left, rect.top)
|
|
130
|
+
path.lineTo(rect.right, rect.top)
|
|
131
|
+
path.moveTo(rect.left, rect.bottom)
|
|
132
|
+
path.lineTo(rect.right, rect.bottom)
|
|
133
|
+
}
|
|
134
|
+
canvas.drawPath(path, sharedBorderPaint)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private fun createRadii(
|
|
138
|
+
isFirst: Boolean,
|
|
139
|
+
isLast: Boolean,
|
|
140
|
+
) = when {
|
|
141
|
+
isFirst && isLast -> {
|
|
142
|
+
floatArrayOf(
|
|
143
|
+
CORNER_RADIUS,
|
|
144
|
+
CORNER_RADIUS,
|
|
145
|
+
CORNER_RADIUS,
|
|
146
|
+
CORNER_RADIUS,
|
|
147
|
+
CORNER_RADIUS,
|
|
148
|
+
CORNER_RADIUS,
|
|
149
|
+
CORNER_RADIUS,
|
|
150
|
+
CORNER_RADIUS,
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
isFirst -> {
|
|
155
|
+
floatArrayOf(CORNER_RADIUS, CORNER_RADIUS, 0f, 0f, 0f, 0f, CORNER_RADIUS, CORNER_RADIUS)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
isLast -> {
|
|
159
|
+
floatArrayOf(0f, 0f, CORNER_RADIUS, CORNER_RADIUS, CORNER_RADIUS, CORNER_RADIUS, 0f, 0f)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
else -> {
|
|
163
|
+
floatArrayOf(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private fun adjustBottomForMargin(
|
|
168
|
+
text: Spanned,
|
|
169
|
+
lineEnd: Int,
|
|
170
|
+
bottom: Int,
|
|
171
|
+
): Int {
|
|
172
|
+
if (lineEnd <= 0 || lineEnd > text.length || text[lineEnd - 1] != '\n') return bottom
|
|
173
|
+
val marginSpans = text.getSpans(lineEnd - 1, lineEnd, MarginBottomSpan::class.java)
|
|
174
|
+
var adjusted = bottom
|
|
175
|
+
for (span in marginSpans) {
|
|
176
|
+
if (text.getSpanEnd(span) == lineEnd) adjusted -= span.marginBottom.toInt()
|
|
177
|
+
}
|
|
178
|
+
return adjusted
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown.spans
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Canvas
|
|
5
|
+
import android.graphics.Paint
|
|
6
|
+
import android.graphics.Path
|
|
7
|
+
import android.graphics.RectF
|
|
8
|
+
import android.text.Layout
|
|
9
|
+
import android.text.Spanned
|
|
10
|
+
import android.text.TextPaint
|
|
11
|
+
import android.text.style.LeadingMarginSpan
|
|
12
|
+
import android.text.style.LineBackgroundSpan
|
|
13
|
+
import android.text.style.MetricAffectingSpan
|
|
14
|
+
import androidx.core.graphics.withSave
|
|
15
|
+
import com.swmansion.enriched.markdown.renderer.BlockStyle
|
|
16
|
+
import com.swmansion.enriched.markdown.renderer.SpanStyleCache
|
|
17
|
+
import com.swmansion.enriched.markdown.styles.CodeBlockStyle
|
|
18
|
+
import com.swmansion.enriched.markdown.utils.applyBlockStyleFont
|
|
19
|
+
import com.swmansion.enriched.markdown.utils.applyColorPreserving
|
|
20
|
+
|
|
21
|
+
class CodeBlockSpan(
|
|
22
|
+
private val codeBlockStyle: CodeBlockStyle,
|
|
23
|
+
private val context: Context,
|
|
24
|
+
private val styleCache: SpanStyleCache,
|
|
25
|
+
) : MetricAffectingSpan(),
|
|
26
|
+
LineBackgroundSpan,
|
|
27
|
+
LeadingMarginSpan {
|
|
28
|
+
private val blockStyle =
|
|
29
|
+
BlockStyle(
|
|
30
|
+
fontSize = codeBlockStyle.fontSize,
|
|
31
|
+
fontFamily = codeBlockStyle.fontFamily,
|
|
32
|
+
fontWeight = codeBlockStyle.fontWeight,
|
|
33
|
+
color = codeBlockStyle.color,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
private val path = Path()
|
|
37
|
+
private val rect = RectF()
|
|
38
|
+
private val arcRect = RectF()
|
|
39
|
+
private val radiiArray = FloatArray(8)
|
|
40
|
+
|
|
41
|
+
companion object {
|
|
42
|
+
private val sharedBackgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL }
|
|
43
|
+
private val sharedBorderPaint =
|
|
44
|
+
Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
|
45
|
+
style = Paint.Style.STROKE
|
|
46
|
+
strokeCap = Paint.Cap.BUTT
|
|
47
|
+
strokeJoin = Paint.Join.ROUND
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private fun configureBackgroundPaint(): Paint =
|
|
52
|
+
sharedBackgroundPaint.apply {
|
|
53
|
+
color = codeBlockStyle.backgroundColor
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private fun configureBorderPaint(): Paint =
|
|
57
|
+
sharedBorderPaint.apply {
|
|
58
|
+
strokeWidth = codeBlockStyle.borderWidth
|
|
59
|
+
color = codeBlockStyle.borderColor
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
override fun getLeadingMargin(first: Boolean): Int = codeBlockStyle.padding.toInt()
|
|
63
|
+
|
|
64
|
+
override fun drawLeadingMargin(
|
|
65
|
+
c: Canvas?,
|
|
66
|
+
p: Paint?,
|
|
67
|
+
x: Int,
|
|
68
|
+
dir: Int,
|
|
69
|
+
top: Int,
|
|
70
|
+
baseline: Int,
|
|
71
|
+
bottom: Int,
|
|
72
|
+
text: CharSequence?,
|
|
73
|
+
start: Int,
|
|
74
|
+
end: Int,
|
|
75
|
+
first: Boolean,
|
|
76
|
+
layout: Layout?,
|
|
77
|
+
) { /* Leading margin is handled by getLeadingMargin */ }
|
|
78
|
+
|
|
79
|
+
override fun updateMeasureState(tp: TextPaint) = applyTextStyle(tp)
|
|
80
|
+
|
|
81
|
+
override fun updateDrawState(tp: TextPaint) = applyTextStyle(tp)
|
|
82
|
+
|
|
83
|
+
override fun drawBackground(
|
|
84
|
+
canvas: Canvas,
|
|
85
|
+
p: Paint,
|
|
86
|
+
left: Int,
|
|
87
|
+
right: Int,
|
|
88
|
+
top: Int,
|
|
89
|
+
baseline: Int,
|
|
90
|
+
bottom: Int,
|
|
91
|
+
text: CharSequence,
|
|
92
|
+
start: Int,
|
|
93
|
+
end: Int,
|
|
94
|
+
lineNum: Int,
|
|
95
|
+
) {
|
|
96
|
+
if (text !is Spanned) return
|
|
97
|
+
|
|
98
|
+
val spanStart = text.getSpanStart(this)
|
|
99
|
+
val spanEnd = text.getSpanEnd(this)
|
|
100
|
+
if (spanStart !in 0 until spanEnd) return
|
|
101
|
+
|
|
102
|
+
val isFirstLine = start == spanStart
|
|
103
|
+
val isLastLine = end == spanEnd || (spanEnd <= end && (spanEnd == text.length || text[spanEnd - 1] == '\n'))
|
|
104
|
+
|
|
105
|
+
val inset = codeBlockStyle.borderWidth / 2f
|
|
106
|
+
|
|
107
|
+
rect.set(
|
|
108
|
+
left.toFloat() + inset,
|
|
109
|
+
top.toFloat() + (if (isFirstLine) inset else 0f),
|
|
110
|
+
right.toFloat() - inset,
|
|
111
|
+
bottom.toFloat() - (if (isLastLine) inset else 0f),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
val radius = codeBlockStyle.borderRadius
|
|
115
|
+
val adjRadius = if (radius > inset) radius - inset else radius
|
|
116
|
+
|
|
117
|
+
// Reset and fill radii array based on boundary state
|
|
118
|
+
radiiArray.fill(0f)
|
|
119
|
+
if (isFirstLine) {
|
|
120
|
+
radiiArray[0] = adjRadius
|
|
121
|
+
radiiArray[1] = adjRadius // Top-Left
|
|
122
|
+
radiiArray[2] = adjRadius
|
|
123
|
+
radiiArray[3] = adjRadius // Top-Right
|
|
124
|
+
}
|
|
125
|
+
if (isLastLine) {
|
|
126
|
+
radiiArray[4] = adjRadius
|
|
127
|
+
radiiArray[5] = adjRadius // Bottom-Right
|
|
128
|
+
radiiArray[6] = adjRadius
|
|
129
|
+
radiiArray[7] = adjRadius // Bottom-Left
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
path.reset()
|
|
133
|
+
path.addRoundRect(rect, radiiArray, Path.Direction.CW)
|
|
134
|
+
|
|
135
|
+
val backgroundPaint = configureBackgroundPaint()
|
|
136
|
+
val borderPaint = configureBorderPaint()
|
|
137
|
+
|
|
138
|
+
canvas.withSave {
|
|
139
|
+
drawPath(path, backgroundPaint)
|
|
140
|
+
|
|
141
|
+
if (codeBlockStyle.borderWidth > 0) {
|
|
142
|
+
val bLeft = rect.left
|
|
143
|
+
val bRight = rect.right
|
|
144
|
+
val bTop = rect.top
|
|
145
|
+
val bBottom = rect.bottom
|
|
146
|
+
|
|
147
|
+
when {
|
|
148
|
+
// Case: Single-line code block
|
|
149
|
+
isFirstLine && isLastLine -> {
|
|
150
|
+
drawPath(path, borderPaint)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Case: Top of a multi-line block
|
|
154
|
+
isFirstLine -> {
|
|
155
|
+
drawLine(bLeft + adjRadius, bTop, bRight - adjRadius, bTop, borderPaint)
|
|
156
|
+
drawLine(bLeft, bTop + adjRadius, bLeft, bBottom, borderPaint)
|
|
157
|
+
drawLine(bRight, bTop + adjRadius, bRight, bBottom, borderPaint)
|
|
158
|
+
|
|
159
|
+
arcRect.set(bLeft, bTop, bLeft + 2 * adjRadius, bTop + 2 * adjRadius)
|
|
160
|
+
drawArc(arcRect, 180f, 90f, false, borderPaint)
|
|
161
|
+
|
|
162
|
+
arcRect.set(bRight - 2 * adjRadius, bTop, bRight, bTop + 2 * adjRadius)
|
|
163
|
+
drawArc(arcRect, 270f, 90f, false, borderPaint)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Case: Bottom of a multi-line block
|
|
167
|
+
isLastLine -> {
|
|
168
|
+
drawLine(bLeft + adjRadius, bBottom, bRight - adjRadius, bBottom, borderPaint)
|
|
169
|
+
drawLine(bLeft, bTop, bLeft, bBottom - adjRadius, borderPaint)
|
|
170
|
+
drawLine(bRight, bTop, bRight, bBottom - adjRadius, borderPaint)
|
|
171
|
+
|
|
172
|
+
arcRect.set(bLeft, bBottom - 2 * adjRadius, bLeft + 2 * adjRadius, bBottom)
|
|
173
|
+
drawArc(arcRect, 90f, 90f, false, borderPaint)
|
|
174
|
+
|
|
175
|
+
arcRect.set(bRight - 2 * adjRadius, bBottom - 2 * adjRadius, bRight, bBottom)
|
|
176
|
+
drawArc(arcRect, 0f, 90f, false, borderPaint)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Case: Middle lines only need vertical sides
|
|
180
|
+
else -> {
|
|
181
|
+
drawLine(bLeft, bTop, bLeft, bBottom, borderPaint)
|
|
182
|
+
drawLine(bRight, bTop, bRight, bBottom, borderPaint)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private fun applyTextStyle(tp: TextPaint) {
|
|
190
|
+
tp.textSize = blockStyle.fontSize
|
|
191
|
+
|
|
192
|
+
tp.applyBlockStyleFont(blockStyle, context)
|
|
193
|
+
|
|
194
|
+
tp.applyColorPreserving(blockStyle.color, *styleCache.colorsToPreserve)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown.spans
|
|
2
|
+
|
|
3
|
+
import android.graphics.Typeface
|
|
4
|
+
import android.text.TextPaint
|
|
5
|
+
import android.text.style.MetricAffectingSpan
|
|
6
|
+
import com.swmansion.enriched.markdown.renderer.BlockStyle
|
|
7
|
+
import com.swmansion.enriched.markdown.renderer.SpanStyleCache
|
|
8
|
+
|
|
9
|
+
class CodeSpan(
|
|
10
|
+
private val styleCache: SpanStyleCache,
|
|
11
|
+
private val blockStyle: BlockStyle,
|
|
12
|
+
) : MetricAffectingSpan() {
|
|
13
|
+
override fun updateDrawState(tp: TextPaint) {
|
|
14
|
+
applyMonospacedFont(tp)
|
|
15
|
+
tp.color = styleCache.codeColor
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
override fun updateMeasureState(tp: TextPaint) {
|
|
19
|
+
applyMonospacedFont(tp)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private fun applyMonospacedFont(paint: TextPaint) {
|
|
23
|
+
paint.textSize = blockStyle.fontSize * 0.85f
|
|
24
|
+
val preservedStyle = (paint.typeface?.style ?: 0) and (Typeface.BOLD or Typeface.ITALIC)
|
|
25
|
+
paint.typeface = SpanStyleCache.getMonospaceTypeface(preservedStyle)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown.spans
|
|
2
|
+
|
|
3
|
+
import android.graphics.Typeface
|
|
4
|
+
import android.text.TextPaint
|
|
5
|
+
import android.text.style.MetricAffectingSpan
|
|
6
|
+
import com.swmansion.enriched.markdown.renderer.BlockStyle
|
|
7
|
+
import com.swmansion.enriched.markdown.renderer.SpanStyleCache
|
|
8
|
+
import com.swmansion.enriched.markdown.utils.applyColorPreserving
|
|
9
|
+
|
|
10
|
+
class EmphasisSpan(
|
|
11
|
+
private val styleCache: SpanStyleCache,
|
|
12
|
+
private val blockStyle: BlockStyle,
|
|
13
|
+
) : MetricAffectingSpan() {
|
|
14
|
+
override fun updateDrawState(tp: TextPaint) {
|
|
15
|
+
applyEmphasisStyle(tp)
|
|
16
|
+
applyEmphasisColor(tp)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
override fun updateMeasureState(tp: TextPaint) {
|
|
20
|
+
applyEmphasisStyle(tp)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private fun applyEmphasisStyle(tp: TextPaint) {
|
|
24
|
+
val currentTypeface = tp.typeface ?: Typeface.DEFAULT
|
|
25
|
+
val isBold = (currentTypeface.style) and Typeface.BOLD != 0
|
|
26
|
+
val style = if (isBold) Typeface.BOLD_ITALIC else Typeface.ITALIC
|
|
27
|
+
tp.typeface = Typeface.create(currentTypeface, style)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private fun applyEmphasisColor(tp: TextPaint) {
|
|
31
|
+
val colorToUse = styleCache.getEmphasisColorFor(blockStyle.color, tp.color)
|
|
32
|
+
tp.applyColorPreserving(colorToUse, *styleCache.colorsToPreserve)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown.spans
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint
|
|
4
|
+
import android.graphics.Typeface
|
|
5
|
+
import android.text.TextPaint
|
|
6
|
+
import android.text.style.MetricAffectingSpan
|
|
7
|
+
import com.swmansion.enriched.markdown.styles.StyleConfig
|
|
8
|
+
|
|
9
|
+
class HeadingSpan(
|
|
10
|
+
val level: Int,
|
|
11
|
+
styleConfig: StyleConfig,
|
|
12
|
+
) : MetricAffectingSpan() {
|
|
13
|
+
private val fontSize: Float = styleConfig.headingStyles[level]!!.fontSize
|
|
14
|
+
private val color: Int = styleConfig.headingStyles[level]!!.color
|
|
15
|
+
private val cachedTypeface: Typeface? = styleConfig.headingTypefaces[level]
|
|
16
|
+
|
|
17
|
+
override fun updateDrawState(tp: TextPaint) {
|
|
18
|
+
applyHeadingStyle(tp)
|
|
19
|
+
tp.color = color
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
override fun updateMeasureState(tp: TextPaint) {
|
|
23
|
+
applyHeadingStyle(tp)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@SuppressLint("WrongConstant") // Result of mask is always valid: 0, 1, 2, or 3
|
|
27
|
+
private fun applyHeadingStyle(tp: TextPaint) {
|
|
28
|
+
tp.textSize = fontSize
|
|
29
|
+
cachedTypeface?.let { base ->
|
|
30
|
+
val preserved = (tp.typeface?.style ?: 0) and BOLD_ITALIC_MASK
|
|
31
|
+
tp.typeface = if (preserved != 0) Typeface.create(base, preserved) else base
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
companion object {
|
|
36
|
+
private const val BOLD_ITALIC_MASK = Typeface.BOLD or Typeface.ITALIC
|
|
37
|
+
}
|
|
38
|
+
}
|