react-native-enriched-markdown-scaffold 0.7.4-scaffold.7 → 0.7.4-scaffold.8

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 (72) hide show
  1. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +36 -0
  2. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathChipStyle.kt +3 -0
  3. package/android/src/main/java/com/swmansion/enriched/markdown/styles/InlineMathStyle.kt +3 -1
  4. package/android/src/main/java/com/swmansion/enriched/markdown/styles/MathFontWeight.kt +25 -0
  5. package/android/src/main/java/com/swmansion/enriched/markdown/styles/MathStyle.kt +12 -1
  6. package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/MarkdownStyleParser.kt +10 -0
  7. package/android/src/math/java/com/swmansion/enriched/markdown/renderer/MathInlineRenderer.kt +1 -0
  8. package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathChipSpan.kt +1 -1
  9. package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathInlineSpan.kt +2 -2
  10. package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathTypeface.kt +30 -0
  11. package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +3 -1
  12. package/docs/INPUT.md +4 -0
  13. package/docs/LATEX_MATH.md +49 -0
  14. package/docs/STYLES.md +2 -0
  15. package/ios/attachments/ENRMMathInlineAttachment+macOS.m +5 -1
  16. package/ios/attachments/ENRMMathInlineAttachment.h +3 -0
  17. package/ios/attachments/ENRMMathInlineAttachment.m +2 -1
  18. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +36 -0
  19. package/ios/input/ENRMInputFormatter.h +4 -0
  20. package/ios/input/ENRMInputFormatter.mm +2 -0
  21. package/ios/input/EnrichedMarkdownTextInput.mm +3 -0
  22. package/ios/input/InputStylePropsUtils.h +13 -0
  23. package/ios/math/ENRMRaTeXBridge.swift +52 -3
  24. package/ios/renderer/ENRMMathInlineRenderer.m +2 -0
  25. package/ios/styles/StyleConfig.h +4 -0
  26. package/ios/styles/StyleConfig.mm +24 -0
  27. package/ios/utils/ENRMMathFontWeight.h +18 -0
  28. package/ios/utils/StylePropsUtils.h +16 -0
  29. package/ios/views/ENRMMathContainerView.m +3 -1
  30. package/lib/module/EnrichedMarkdownNativeComponent.ts +2 -0
  31. package/lib/module/EnrichedMarkdownTextInput.js.map +1 -1
  32. package/lib/module/EnrichedMarkdownTextInputNativeComponent.ts +1 -0
  33. package/lib/module/EnrichedMarkdownTextNativeComponent.ts +2 -0
  34. package/lib/module/normalizeMarkdownStyle.js +4 -2
  35. package/lib/module/normalizeMarkdownStyle.js.map +1 -1
  36. package/lib/module/normalizeMarkdownStyle.web.js +4 -2
  37. package/lib/module/normalizeMarkdownStyle.web.js.map +1 -1
  38. package/lib/module/normalizeMarkdownTextInputStyle.js +7 -3
  39. package/lib/module/normalizeMarkdownTextInputStyle.js.map +1 -1
  40. package/lib/module/web/globalStyles.js +8 -1
  41. package/lib/module/web/globalStyles.js.map +1 -1
  42. package/lib/module/web/styles.js +3 -1
  43. package/lib/module/web/styles.js.map +1 -1
  44. package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +2 -0
  45. package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
  46. package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts +1 -0
  47. package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts.map +1 -1
  48. package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts +1 -0
  49. package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts.map +1 -1
  50. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +2 -0
  51. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
  52. package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -1
  53. package/lib/typescript/src/normalizeMarkdownStyle.web.d.ts.map +1 -1
  54. package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts +1 -0
  55. package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts.map +1 -1
  56. package/lib/typescript/src/types/MarkdownStyle.d.ts +24 -0
  57. package/lib/typescript/src/types/MarkdownStyle.d.ts.map +1 -1
  58. package/lib/typescript/src/types/MarkdownStyleInternal.d.ts +2 -0
  59. package/lib/typescript/src/types/MarkdownStyleInternal.d.ts.map +1 -1
  60. package/lib/typescript/src/web/styles.d.ts.map +1 -1
  61. package/package.json +1 -1
  62. package/src/EnrichedMarkdownNativeComponent.ts +2 -0
  63. package/src/EnrichedMarkdownTextInput.tsx +1 -0
  64. package/src/EnrichedMarkdownTextInputNativeComponent.ts +1 -0
  65. package/src/EnrichedMarkdownTextNativeComponent.ts +2 -0
  66. package/src/normalizeMarkdownStyle.ts +6 -1
  67. package/src/normalizeMarkdownStyle.web.ts +6 -1
  68. package/src/normalizeMarkdownTextInputStyle.ts +5 -0
  69. package/src/types/MarkdownStyle.ts +24 -0
  70. package/src/types/MarkdownStyleInternal.ts +2 -0
  71. package/src/web/globalStyles.ts +7 -0
  72. package/src/web/styles.ts +5 -1
@@ -1534,6 +1534,7 @@ struct EnrichedMarkdownMarkdownStyleMathStruct {
1534
1534
  Float marginTop{0.0};
1535
1535
  Float marginBottom{0.0};
1536
1536
  std::string textAlign{};
1537
+ std::string fontWeight{};
1537
1538
 
1538
1539
 
1539
1540
  #ifdef RN_SERIALIZABLE_STATE
@@ -1548,6 +1549,7 @@ struct EnrichedMarkdownMarkdownStyleMathStruct {
1548
1549
  result["marginTop"] = marginTop;
1549
1550
  result["marginBottom"] = marginBottom;
1550
1551
  result["textAlign"] = textAlign;
1552
+ result["fontWeight"] = fontWeight;
1551
1553
  return result;
1552
1554
  }
1553
1555
  #endif
@@ -1584,6 +1586,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
1584
1586
  if (tmp_textAlign != map.end()) {
1585
1587
  fromRawValue(context, tmp_textAlign->second, result.textAlign);
1586
1588
  }
1589
+ auto tmp_fontWeight = map.find("fontWeight");
1590
+ if (tmp_fontWeight != map.end()) {
1591
+ fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
1592
+ }
1587
1593
  }
1588
1594
 
1589
1595
  static inline std::string toString(const EnrichedMarkdownMarkdownStyleMathStruct &value) {
@@ -1599,6 +1605,7 @@ static inline folly::dynamic toDynamic(const EnrichedMarkdownMarkdownStyleMathSt
1599
1605
  struct EnrichedMarkdownMarkdownStyleInlineMathStruct {
1600
1606
  SharedColor color{};
1601
1607
  Float paddingVertical{0.0};
1608
+ std::string fontWeight{};
1602
1609
 
1603
1610
 
1604
1611
  #ifdef RN_SERIALIZABLE_STATE
@@ -1608,6 +1615,7 @@ struct EnrichedMarkdownMarkdownStyleInlineMathStruct {
1608
1615
  folly::dynamic result = folly::dynamic::object();
1609
1616
  result["color"] = ::facebook::react::toDynamic(color);
1610
1617
  result["paddingVertical"] = paddingVertical;
1618
+ result["fontWeight"] = fontWeight;
1611
1619
  return result;
1612
1620
  }
1613
1621
  #endif
@@ -1624,6 +1632,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
1624
1632
  if (tmp_paddingVertical != map.end()) {
1625
1633
  fromRawValue(context, tmp_paddingVertical->second, result.paddingVertical);
1626
1634
  }
1635
+ auto tmp_fontWeight = map.find("fontWeight");
1636
+ if (tmp_fontWeight != map.end()) {
1637
+ fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
1638
+ }
1627
1639
  }
1628
1640
 
1629
1641
  static inline std::string toString(const EnrichedMarkdownMarkdownStyleInlineMathStruct &value) {
@@ -2810,6 +2822,7 @@ struct EnrichedMarkdownTextInputMarkdownStyleMathStruct {
2810
2822
  Float paddingVertical{0.0};
2811
2823
  SharedColor selectedBackgroundColor{};
2812
2824
  SharedColor selectedBorderColor{};
2825
+ std::string fontWeight{};
2813
2826
 
2814
2827
 
2815
2828
  #ifdef RN_SERIALIZABLE_STATE
@@ -2827,6 +2840,7 @@ struct EnrichedMarkdownTextInputMarkdownStyleMathStruct {
2827
2840
  result["paddingVertical"] = paddingVertical;
2828
2841
  result["selectedBackgroundColor"] = ::facebook::react::toDynamic(selectedBackgroundColor);
2829
2842
  result["selectedBorderColor"] = ::facebook::react::toDynamic(selectedBorderColor);
2843
+ result["fontWeight"] = fontWeight;
2830
2844
  return result;
2831
2845
  }
2832
2846
  #endif
@@ -2875,6 +2889,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
2875
2889
  if (tmp_selectedBorderColor != map.end()) {
2876
2890
  fromRawValue(context, tmp_selectedBorderColor->second, result.selectedBorderColor);
2877
2891
  }
2892
+ auto tmp_fontWeight = map.find("fontWeight");
2893
+ if (tmp_fontWeight != map.end()) {
2894
+ fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
2895
+ }
2878
2896
  }
2879
2897
 
2880
2898
  static inline std::string toString(const EnrichedMarkdownTextInputMarkdownStyleMathStruct &value) {
@@ -2898,6 +2916,7 @@ struct EnrichedMarkdownTextInputMarkdownStyleMathBlockStruct {
2898
2916
  Float paddingVertical{0.0};
2899
2917
  SharedColor selectedBackgroundColor{};
2900
2918
  SharedColor selectedBorderColor{};
2919
+ std::string fontWeight{};
2901
2920
  Float marginTop{0.0};
2902
2921
  Float marginBottom{0.0};
2903
2922
  std::string alignment{};
@@ -2918,6 +2937,7 @@ struct EnrichedMarkdownTextInputMarkdownStyleMathBlockStruct {
2918
2937
  result["paddingVertical"] = paddingVertical;
2919
2938
  result["selectedBackgroundColor"] = ::facebook::react::toDynamic(selectedBackgroundColor);
2920
2939
  result["selectedBorderColor"] = ::facebook::react::toDynamic(selectedBorderColor);
2940
+ result["fontWeight"] = fontWeight;
2921
2941
  result["marginTop"] = marginTop;
2922
2942
  result["marginBottom"] = marginBottom;
2923
2943
  result["alignment"] = alignment;
@@ -2969,6 +2989,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
2969
2989
  if (tmp_selectedBorderColor != map.end()) {
2970
2990
  fromRawValue(context, tmp_selectedBorderColor->second, result.selectedBorderColor);
2971
2991
  }
2992
+ auto tmp_fontWeight = map.find("fontWeight");
2993
+ if (tmp_fontWeight != map.end()) {
2994
+ fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
2995
+ }
2972
2996
  auto tmp_marginTop = map.find("marginTop");
2973
2997
  if (tmp_marginTop != map.end()) {
2974
2998
  fromRawValue(context, tmp_marginTop->second, result.marginTop);
@@ -4878,6 +4902,7 @@ struct EnrichedMarkdownTextMarkdownStyleMathStruct {
4878
4902
  Float marginTop{0.0};
4879
4903
  Float marginBottom{0.0};
4880
4904
  std::string textAlign{};
4905
+ std::string fontWeight{};
4881
4906
 
4882
4907
 
4883
4908
  #ifdef RN_SERIALIZABLE_STATE
@@ -4892,6 +4917,7 @@ struct EnrichedMarkdownTextMarkdownStyleMathStruct {
4892
4917
  result["marginTop"] = marginTop;
4893
4918
  result["marginBottom"] = marginBottom;
4894
4919
  result["textAlign"] = textAlign;
4920
+ result["fontWeight"] = fontWeight;
4895
4921
  return result;
4896
4922
  }
4897
4923
  #endif
@@ -4928,6 +4954,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
4928
4954
  if (tmp_textAlign != map.end()) {
4929
4955
  fromRawValue(context, tmp_textAlign->second, result.textAlign);
4930
4956
  }
4957
+ auto tmp_fontWeight = map.find("fontWeight");
4958
+ if (tmp_fontWeight != map.end()) {
4959
+ fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
4960
+ }
4931
4961
  }
4932
4962
 
4933
4963
  static inline std::string toString(const EnrichedMarkdownTextMarkdownStyleMathStruct &value) {
@@ -4943,6 +4973,7 @@ static inline folly::dynamic toDynamic(const EnrichedMarkdownTextMarkdownStyleMa
4943
4973
  struct EnrichedMarkdownTextMarkdownStyleInlineMathStruct {
4944
4974
  SharedColor color{};
4945
4975
  Float paddingVertical{0.0};
4976
+ std::string fontWeight{};
4946
4977
 
4947
4978
 
4948
4979
  #ifdef RN_SERIALIZABLE_STATE
@@ -4952,6 +4983,7 @@ struct EnrichedMarkdownTextMarkdownStyleInlineMathStruct {
4952
4983
  folly::dynamic result = folly::dynamic::object();
4953
4984
  result["color"] = ::facebook::react::toDynamic(color);
4954
4985
  result["paddingVertical"] = paddingVertical;
4986
+ result["fontWeight"] = fontWeight;
4955
4987
  return result;
4956
4988
  }
4957
4989
  #endif
@@ -4968,6 +5000,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
4968
5000
  if (tmp_paddingVertical != map.end()) {
4969
5001
  fromRawValue(context, tmp_paddingVertical->second, result.paddingVertical);
4970
5002
  }
5003
+ auto tmp_fontWeight = map.find("fontWeight");
5004
+ if (tmp_fontWeight != map.end()) {
5005
+ fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
5006
+ }
4971
5007
  }
4972
5008
 
4973
5009
  static inline std::string toString(const EnrichedMarkdownTextMarkdownStyleInlineMathStruct &value) {
@@ -13,6 +13,7 @@ open class MathChipStyle(
13
13
  val paddingVertical: Float,
14
14
  val selectedBackgroundColor: Int?,
15
15
  val selectedBorderColor: Int?,
16
+ val fontWeight: String = "normal",
16
17
  ) {
17
18
  open fun isBlock(): Boolean = false
18
19
  }
@@ -28,6 +29,7 @@ class MathBlockStyle(
28
29
  paddingVertical: Float,
29
30
  selectedBackgroundColor: Int?,
30
31
  selectedBorderColor: Int?,
32
+ fontWeight: String = "normal",
31
33
  val marginTop: Float,
32
34
  val marginBottom: Float,
33
35
  val alignment: Layout.Alignment,
@@ -42,6 +44,7 @@ class MathBlockStyle(
42
44
  paddingVertical,
43
45
  selectedBackgroundColor,
44
46
  selectedBorderColor,
47
+ fontWeight,
45
48
  ) {
46
49
  override fun isBlock(): Boolean = true
47
50
  }
@@ -5,6 +5,7 @@ import com.facebook.react.bridge.ReadableMap
5
5
  data class InlineMathStyle(
6
6
  val color: Int,
7
7
  val paddingVertical: Float,
8
+ val fontWeight: String,
8
9
  ) {
9
10
  companion object {
10
11
  fun fromReadableMap(
@@ -13,7 +14,8 @@ data class InlineMathStyle(
13
14
  ): InlineMathStyle {
14
15
  val color = parser.parseColor(map, "color")
15
16
  val paddingVertical = parser.toPixelFromDIP(map.getDouble("paddingVertical").toFloat())
16
- return InlineMathStyle(color, paddingVertical)
17
+ val fontWeight = parser.parseString(map, "fontWeight", "normal")
18
+ return InlineMathStyle(color, paddingVertical, fontWeight)
17
19
  }
18
20
  }
19
21
  }
@@ -0,0 +1,25 @@
1
+ package com.swmansion.enriched.markdown.styles
2
+
3
+ /**
4
+ * Weight a formula can actually be typeset at.
5
+ *
6
+ * The style model keeps `fontWeight` as a [String], the way every other
7
+ * `fontWeight` prop in the codebase does, so a value like `"600"` survives as
8
+ * far as the renderer. This enum is the narrowing step at that last hop — see
9
+ * `ratexTypefaceLoader`, the only caller — so adding a third weight is a
10
+ * compile error at the draw site rather than a value that silently falls back
11
+ * to regular. Mirrors ENRMMathFontWeight.h on iOS.
12
+ */
13
+ enum class MathFontWeight {
14
+ NORMAL,
15
+ BOLD,
16
+ ;
17
+
18
+ companion object {
19
+ fun fromString(value: String): MathFontWeight =
20
+ when (value.lowercase()) {
21
+ "bold" -> BOLD
22
+ else -> NORMAL
23
+ }
24
+ }
25
+ }
@@ -10,6 +10,7 @@ data class MathStyle(
10
10
  val marginTop: Float,
11
11
  val marginBottom: Float,
12
12
  val textAlign: String,
13
+ val fontWeight: String,
13
14
  ) {
14
15
  companion object {
15
16
  fun fromReadableMap(
@@ -23,7 +24,17 @@ data class MathStyle(
23
24
  val marginTop = parser.toPixelFromDIP(map.getDouble("marginTop").toFloat())
24
25
  val marginBottom = parser.toPixelFromDIP(map.getDouble("marginBottom").toFloat())
25
26
  val textAlign = parser.parseString(map, "textAlign", "center")
26
- return MathStyle(fontSize, color, backgroundColor, padding, marginTop, marginBottom, textAlign)
27
+ val fontWeight = parser.parseString(map, "fontWeight", "normal")
28
+ return MathStyle(
29
+ fontSize,
30
+ color,
31
+ backgroundColor,
32
+ padding,
33
+ marginTop,
34
+ marginBottom,
35
+ textAlign,
36
+ fontWeight,
37
+ )
27
38
  }
28
39
  }
29
40
  }
@@ -76,6 +76,8 @@ object MarkdownStyleParser {
76
76
  if (mathMap?.hasKey("selectedBackgroundColor") == true) mathMap.getInt("selectedBackgroundColor") else null
77
77
  val selectedBorderColor =
78
78
  if (mathMap?.hasKey("selectedBorderColor") == true) mathMap.getInt("selectedBorderColor") else null
79
+ val fontWeight =
80
+ (if (mathMap?.hasKey("fontWeight") == true) mathMap.getString("fontWeight") else null) ?: "normal"
79
81
 
80
82
  return MathChipStyle(
81
83
  color = color,
@@ -88,6 +90,7 @@ object MarkdownStyleParser {
88
90
  paddingVertical = paddingVertical,
89
91
  selectedBackgroundColor = nullIfTransparent(selectedBackgroundColor),
90
92
  selectedBorderColor = nullIfTransparent(selectedBorderColor),
93
+ fontWeight = fontWeight,
91
94
  )
92
95
  }
93
96
 
@@ -143,6 +146,12 @@ object MarkdownStyleParser {
143
146
  } else {
144
147
  inlineFallback.selectedBorderColor
145
148
  }
149
+ val fontWeight =
150
+ if (mathBlockMap?.hasKey("fontWeight") == true) {
151
+ mathBlockMap.getString("fontWeight") ?: "normal"
152
+ } else {
153
+ inlineFallback.fontWeight
154
+ }
146
155
  val marginTop =
147
156
  PixelUtil.toPixelFromDIP(
148
157
  if (mathBlockMap?.hasKey("marginTop") == true) mathBlockMap.getDouble("marginTop").toFloat() else 8f,
@@ -170,6 +179,7 @@ object MarkdownStyleParser {
170
179
  paddingVertical = paddingVertical,
171
180
  selectedBackgroundColor = nullIfTransparent(selectedBackgroundColor),
172
181
  selectedBorderColor = nullIfTransparent(selectedBorderColor),
182
+ fontWeight = fontWeight,
173
183
  marginTop = marginTop,
174
184
  marginBottom = marginBottom,
175
185
  alignment = alignment,
@@ -36,6 +36,7 @@ class MathInlineRenderer(
36
36
  fontSize = blockStyle.fontSize,
37
37
  textColor = config.style.inlineMathStyle.color,
38
38
  paddingVertical = config.style.inlineMathStyle.paddingVertical,
39
+ fontWeight = config.style.inlineMathStyle.fontWeight,
39
40
  )
40
41
 
41
42
  builder.setSpan(span, start, end, SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE)
@@ -66,7 +66,7 @@ class MathChipSpan(
66
66
  displayMode = displayMode,
67
67
  color = style.color,
68
68
  )
69
- val renderer = RaTeXRenderer(displayList, style.fontSize) { RaTeXFontLoader.getTypeface(it) }
69
+ val renderer = RaTeXRenderer(displayList, style.fontSize, ratexTypefaceLoader(style.fontWeight))
70
70
  formulaWidth = renderer.widthPx
71
71
  formulaAscent = renderer.heightPx
72
72
  formulaDescent = renderer.depthPx
@@ -6,7 +6,6 @@ import android.graphics.Canvas
6
6
  import android.graphics.Paint
7
7
  import android.text.style.ReplacementSpan
8
8
  import io.ratex.RaTeXEngine
9
- import io.ratex.RaTeXFontLoader
10
9
  import io.ratex.RaTeXRenderer
11
10
  import kotlin.math.ceil
12
11
 
@@ -16,6 +15,7 @@ class MathInlineSpan(
16
15
  internal val fontSize: Float,
17
16
  private val textColor: Int,
18
17
  private val paddingVertical: Float,
18
+ private val fontWeight: String = "normal",
19
19
  ) : ReplacementSpan() {
20
20
  private var cachedBitmap: Bitmap? = null
21
21
  private var cachedWidth = 0
@@ -29,7 +29,7 @@ class MathInlineSpan(
29
29
 
30
30
  try {
31
31
  val displayList = RaTeXEngine.parseBlocking(latex, displayMode = false, color = textColor)
32
- val renderer = RaTeXRenderer(displayList, fontSize) { RaTeXFontLoader.getTypeface(it) }
32
+ val renderer = RaTeXRenderer(displayList, fontSize, ratexTypefaceLoader(fontWeight))
33
33
 
34
34
  cachedWidth = renderer.widthPx.toInt().coerceAtLeast(1)
35
35
  mathAscent = renderer.heightPx
@@ -0,0 +1,30 @@
1
+ package com.swmansion.enriched.markdown.spans
2
+
3
+ import android.graphics.Typeface
4
+ import com.swmansion.enriched.markdown.styles.MathFontWeight
5
+ import io.ratex.RaTeXFontLoader
6
+
7
+ /**
8
+ * Typeface supplier for [io.ratex.RaTeXRenderer]. RaTeX resolves every glyph to
9
+ * a KaTeX font id and asks us for the face to draw it with.
10
+ *
11
+ * Takes the raw `fontWeight` string the style model carries — the same shape
12
+ * every other `fontWeight` prop keeps — and is the single place on Android that
13
+ * narrows it to a weight we can draw. Consumers pass the style through; nothing
14
+ * upstream has to know that only two weights are reachable today.
15
+ *
16
+ * Bold is SYNTHETIC: asking for [Typeface.BOLD] on a single-weight asset makes
17
+ * the platform embolden it, which preserves the advances RaTeX already laid the
18
+ * glyphs out with. Substituting the bundled KaTeX bold faces instead would not
19
+ * — their advances run up to 15% wider than the regular ones the layout
20
+ * reserved, so glyphs would collide. iOS reaches the same result by stroking
21
+ * the glyph outline; see ENRMRaTeXBridge.
22
+ */
23
+ internal fun ratexTypefaceLoader(fontWeight: String): (String) -> Typeface? =
24
+ when (MathFontWeight.fromString(fontWeight)) {
25
+ MathFontWeight.NORMAL -> { fontId -> RaTeXFontLoader.getTypeface(fontId) }
26
+
27
+ MathFontWeight.BOLD -> { fontId ->
28
+ RaTeXFontLoader.getTypeface(fontId)?.let { Typeface.create(it, Typeface.BOLD) }
29
+ }
30
+ }
@@ -15,6 +15,7 @@ import com.swmansion.enriched.markdown.accessibility.AccessibilityLabels
15
15
  import com.swmansion.enriched.markdown.spans.MathMeasureHelper
16
16
  import com.swmansion.enriched.markdown.spans.MathMeasureRequest
17
17
  import com.swmansion.enriched.markdown.spans.MathRenderMode
18
+ import com.swmansion.enriched.markdown.spans.ratexTypefaceLoader
18
19
  import com.swmansion.enriched.markdown.styles.MathStyle
19
20
  import com.swmansion.enriched.markdown.styles.StyleConfig
20
21
  import io.ratex.RaTeXEngine
@@ -100,7 +101,8 @@ class MathContainerView(
100
101
  cachedLatex = latex
101
102
  try {
102
103
  val displayList = RaTeXEngine.parseBlocking(latex, displayMode = true, color = mathStyle.color)
103
- mathView.renderer = RaTeXRenderer(displayList, mathStyle.fontSize) { RaTeXFontLoader.getTypeface(it) }
104
+ mathView.renderer =
105
+ RaTeXRenderer(displayList, mathStyle.fontSize, ratexTypefaceLoader(mathStyle.fontWeight))
104
106
  } catch (e: Exception) {
105
107
  Log.e(TAG, "Failed to render LaTeX", e)
106
108
  mathView.renderer = null
package/docs/INPUT.md CHANGED
@@ -231,3 +231,7 @@ Available style properties:
231
231
  - `link.underline` — whether links are underlined (defaults to `true`).
232
232
  - `spoiler.color` — text color for spoiler text.
233
233
  - `spoiler.backgroundColor` — background color for spoiler text.
234
+ - `math.fontWeight` — `'bold'` | `'normal'` (default `'normal'`); typesets inline formula chips
235
+ (`$...$`) at bold weight. See [LATEX_MATH.md](./LATEX_MATH.md#bold-formulas).
236
+ - `mathBlock.fontWeight` — same for block formula chips (`$$...$$`); inherits from
237
+ `math.fontWeight` when unset.
@@ -47,6 +47,50 @@ what pushes those lines apart. It affects only lines that actually contain inlin
47
47
  the line height they had. Set it to `0` to get the old flush behaviour back. Block math (`$$...$$`) has its own
48
48
  `math.padding` instead.
49
49
 
50
+ ### Bold formulas
51
+
52
+ `math.fontWeight` / `inlineMath.fontWeight` (`'normal'` | `'bold'`, default `'normal'`) typeset a
53
+ formula at bold weight. Set it wherever the surrounding text is bold so the formula follows it.
54
+
55
+ The embolden is **synthetic on every platform** — no real bold face is ever used.
56
+
57
+ On **iOS and Android** it is a stroke on the glyph outline and the platform's fake-bold
58
+ respectively, rather than a switch to the bundled KaTeX bold faces. That is deliberate. The native
59
+ engine has no weight parameter, and by the time a style applies, glyph positions are already laid
60
+ out: the bold faces' advances run up to 15% wider than the regular ones the layout reserved, so
61
+ swapping them in would make glyphs collide. Stroking and fake-bold both preserve every advance.
62
+
63
+ Two consequences worth knowing:
64
+
65
+ - Fraction bars and rules keep their own thickness, so they do not thicken with the glyphs.
66
+ - On iOS the stroke width also applies to the handful of decorations the engine strokes itself
67
+ (`\cancel`, `\textcircled`, `\corresponds`, `\imageof`). Prefer turning bold on per surface
68
+ rather than globally.
69
+
70
+ On **web** the prop is a plain CSS declaration on the KaTeX output — but the library renders in
71
+ [MathML output mode](#web) and loads no KaTeX stylesheet or fonts, so `font-weight` is resolved by
72
+ the browser's own math font (STIX Two Math, Latin Modern Math, Cambria Math). Most of those ship no
73
+ bold face, so the browser synthesizes one there too. Treat web as synthetic as well, not as a
74
+ reference for how heavy bold "should" look.
75
+
76
+ `EnrichedMarkdownTextInput` honours the same prop for the formula chips typed into the editor, under
77
+ its own style keys — `math.fontWeight` for inline chips (`$...$`) and `mathBlock.fontWeight` for
78
+ block chips (`$$...$$`). `mathBlock` inherits the weight from `math` when it does not set one of its
79
+ own, the way it already inherits `color` and the border props:
80
+
81
+ ```tsx
82
+ <EnrichedMarkdownTextInput
83
+ markdownStyle={{
84
+ math: { fontWeight: 'bold' }, // inline chips bold…
85
+ mathBlock: { fontWeight: 'normal' }, // …block chips opt back out
86
+ }}
87
+ />
88
+ ```
89
+
90
+ The caveats above apply unchanged — the embolden is the same synthetic one, and the chip keeps its
91
+ measured width because fake-bold and stroking both preserve the glyph advances. Editor chips are
92
+ iOS + Android only; there is no web or macOS chip surface.
93
+
50
94
  > [!IMPORTANT]
51
95
  > LaTeX commands use backslashes (e.g. `\frac`, `\alpha`). In regular JS strings and template literals, backslashes are escape characters. Use `String.raw` or double backslashes (`\\frac`) to preserve them. Block math (`$$...$$`) must be on its own line to render as a display element.
52
96
 
@@ -66,6 +110,11 @@ yarn add katex
66
110
 
67
111
  KaTeX is loaded lazily the first time a LaTeX node is encountered, so it has no impact on pages that do not render math. No stylesheet or `<link>` tag is needed.
68
112
 
113
+ If you load `katex/dist/katex.min.css` anyway — some setups do — its `.katex` rule opens with the
114
+ `font` shorthand, which resets `font-weight` and would drop [`fontWeight`](#bold-formulas) on the
115
+ way down to the formula. The library injects `.katex { font-weight: inherit; }` to keep the prop
116
+ working in that case; nothing is required of you.
117
+
69
118
  > [!NOTE]
70
119
  > MathML is supported natively in Chrome 109+, Firefox, and Safari. Older browsers will display the raw LaTeX source as a text fallback.
71
120
 
package/docs/STYLES.md CHANGED
@@ -179,10 +179,12 @@ The library provides sensible default styles for all Markdown elements out of th
179
179
  padding: 12,
180
180
  marginBottom: 16,
181
181
  textAlign: 'center',
182
+ fontWeight: 'normal',
182
183
  },
183
184
  inlineMath: {
184
185
  color: '#1F2937',
185
186
  paddingVertical: 4,
187
+ fontWeight: 'normal',
186
188
  },
187
189
  spoiler: {
188
190
  color: '#6B7280',
@@ -25,7 +25,11 @@
25
25
  - (void)renderForMacOS
26
26
  {
27
27
  RCTUIColor *color = self.mathTextColor ?: [RCTUIColor blackColor];
28
- ENRMRaTeXRenderResult *result = [ENRMRaTeXBridge parse:self.latex displayMode:NO fontSize:self.fontSize color:color];
28
+ ENRMRaTeXRenderResult *result = [ENRMRaTeXBridge parse:self.latex
29
+ displayMode:NO
30
+ fontSize:self.fontSize
31
+ color:color
32
+ bold:self.bold];
29
33
  if (!result)
30
34
  return;
31
35
 
@@ -9,6 +9,9 @@ NS_ASSUME_NONNULL_BEGIN
9
9
  @property (nonatomic, assign) CGFloat fontSize;
10
10
  @property (nonatomic, strong, nullable) RCTUIColor *mathTextColor;
11
11
 
12
+ /// Typeset the glyphs at bold weight (synthetic — see ENRMRaTeXBridge).
13
+ @property (nonatomic, assign) BOOL bold;
14
+
12
15
  /// YES if block math (`$$...$$`), NO for inline (`$...$`).
13
16
  @property (nonatomic, assign) BOOL displayMode;
14
17
 
@@ -21,7 +21,8 @@
21
21
  ENRMRaTeXRenderResult *result = [ENRMRaTeXBridge parse:self.latex
22
22
  displayMode:self.displayMode
23
23
  fontSize:self.fontSize
24
- color:color];
24
+ color:color
25
+ bold:self.bold];
25
26
  if (!result)
26
27
  return;
27
28
 
@@ -1534,6 +1534,7 @@ struct EnrichedMarkdownMarkdownStyleMathStruct {
1534
1534
  Float marginTop{0.0};
1535
1535
  Float marginBottom{0.0};
1536
1536
  std::string textAlign{};
1537
+ std::string fontWeight{};
1537
1538
 
1538
1539
 
1539
1540
  #ifdef RN_SERIALIZABLE_STATE
@@ -1548,6 +1549,7 @@ struct EnrichedMarkdownMarkdownStyleMathStruct {
1548
1549
  result["marginTop"] = marginTop;
1549
1550
  result["marginBottom"] = marginBottom;
1550
1551
  result["textAlign"] = textAlign;
1552
+ result["fontWeight"] = fontWeight;
1551
1553
  return result;
1552
1554
  }
1553
1555
  #endif
@@ -1584,6 +1586,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
1584
1586
  if (tmp_textAlign != map.end()) {
1585
1587
  fromRawValue(context, tmp_textAlign->second, result.textAlign);
1586
1588
  }
1589
+ auto tmp_fontWeight = map.find("fontWeight");
1590
+ if (tmp_fontWeight != map.end()) {
1591
+ fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
1592
+ }
1587
1593
  }
1588
1594
 
1589
1595
  static inline std::string toString(const EnrichedMarkdownMarkdownStyleMathStruct &value) {
@@ -1599,6 +1605,7 @@ static inline folly::dynamic toDynamic(const EnrichedMarkdownMarkdownStyleMathSt
1599
1605
  struct EnrichedMarkdownMarkdownStyleInlineMathStruct {
1600
1606
  SharedColor color{};
1601
1607
  Float paddingVertical{0.0};
1608
+ std::string fontWeight{};
1602
1609
 
1603
1610
 
1604
1611
  #ifdef RN_SERIALIZABLE_STATE
@@ -1608,6 +1615,7 @@ struct EnrichedMarkdownMarkdownStyleInlineMathStruct {
1608
1615
  folly::dynamic result = folly::dynamic::object();
1609
1616
  result["color"] = ::facebook::react::toDynamic(color);
1610
1617
  result["paddingVertical"] = paddingVertical;
1618
+ result["fontWeight"] = fontWeight;
1611
1619
  return result;
1612
1620
  }
1613
1621
  #endif
@@ -1624,6 +1632,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
1624
1632
  if (tmp_paddingVertical != map.end()) {
1625
1633
  fromRawValue(context, tmp_paddingVertical->second, result.paddingVertical);
1626
1634
  }
1635
+ auto tmp_fontWeight = map.find("fontWeight");
1636
+ if (tmp_fontWeight != map.end()) {
1637
+ fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
1638
+ }
1627
1639
  }
1628
1640
 
1629
1641
  static inline std::string toString(const EnrichedMarkdownMarkdownStyleInlineMathStruct &value) {
@@ -2810,6 +2822,7 @@ struct EnrichedMarkdownTextInputMarkdownStyleMathStruct {
2810
2822
  Float paddingVertical{0.0};
2811
2823
  SharedColor selectedBackgroundColor{};
2812
2824
  SharedColor selectedBorderColor{};
2825
+ std::string fontWeight{};
2813
2826
 
2814
2827
 
2815
2828
  #ifdef RN_SERIALIZABLE_STATE
@@ -2827,6 +2840,7 @@ struct EnrichedMarkdownTextInputMarkdownStyleMathStruct {
2827
2840
  result["paddingVertical"] = paddingVertical;
2828
2841
  result["selectedBackgroundColor"] = ::facebook::react::toDynamic(selectedBackgroundColor);
2829
2842
  result["selectedBorderColor"] = ::facebook::react::toDynamic(selectedBorderColor);
2843
+ result["fontWeight"] = fontWeight;
2830
2844
  return result;
2831
2845
  }
2832
2846
  #endif
@@ -2875,6 +2889,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
2875
2889
  if (tmp_selectedBorderColor != map.end()) {
2876
2890
  fromRawValue(context, tmp_selectedBorderColor->second, result.selectedBorderColor);
2877
2891
  }
2892
+ auto tmp_fontWeight = map.find("fontWeight");
2893
+ if (tmp_fontWeight != map.end()) {
2894
+ fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
2895
+ }
2878
2896
  }
2879
2897
 
2880
2898
  static inline std::string toString(const EnrichedMarkdownTextInputMarkdownStyleMathStruct &value) {
@@ -2898,6 +2916,7 @@ struct EnrichedMarkdownTextInputMarkdownStyleMathBlockStruct {
2898
2916
  Float paddingVertical{0.0};
2899
2917
  SharedColor selectedBackgroundColor{};
2900
2918
  SharedColor selectedBorderColor{};
2919
+ std::string fontWeight{};
2901
2920
  Float marginTop{0.0};
2902
2921
  Float marginBottom{0.0};
2903
2922
  std::string alignment{};
@@ -2918,6 +2937,7 @@ struct EnrichedMarkdownTextInputMarkdownStyleMathBlockStruct {
2918
2937
  result["paddingVertical"] = paddingVertical;
2919
2938
  result["selectedBackgroundColor"] = ::facebook::react::toDynamic(selectedBackgroundColor);
2920
2939
  result["selectedBorderColor"] = ::facebook::react::toDynamic(selectedBorderColor);
2940
+ result["fontWeight"] = fontWeight;
2921
2941
  result["marginTop"] = marginTop;
2922
2942
  result["marginBottom"] = marginBottom;
2923
2943
  result["alignment"] = alignment;
@@ -2969,6 +2989,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
2969
2989
  if (tmp_selectedBorderColor != map.end()) {
2970
2990
  fromRawValue(context, tmp_selectedBorderColor->second, result.selectedBorderColor);
2971
2991
  }
2992
+ auto tmp_fontWeight = map.find("fontWeight");
2993
+ if (tmp_fontWeight != map.end()) {
2994
+ fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
2995
+ }
2972
2996
  auto tmp_marginTop = map.find("marginTop");
2973
2997
  if (tmp_marginTop != map.end()) {
2974
2998
  fromRawValue(context, tmp_marginTop->second, result.marginTop);
@@ -4878,6 +4902,7 @@ struct EnrichedMarkdownTextMarkdownStyleMathStruct {
4878
4902
  Float marginTop{0.0};
4879
4903
  Float marginBottom{0.0};
4880
4904
  std::string textAlign{};
4905
+ std::string fontWeight{};
4881
4906
 
4882
4907
 
4883
4908
  #ifdef RN_SERIALIZABLE_STATE
@@ -4892,6 +4917,7 @@ struct EnrichedMarkdownTextMarkdownStyleMathStruct {
4892
4917
  result["marginTop"] = marginTop;
4893
4918
  result["marginBottom"] = marginBottom;
4894
4919
  result["textAlign"] = textAlign;
4920
+ result["fontWeight"] = fontWeight;
4895
4921
  return result;
4896
4922
  }
4897
4923
  #endif
@@ -4928,6 +4954,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
4928
4954
  if (tmp_textAlign != map.end()) {
4929
4955
  fromRawValue(context, tmp_textAlign->second, result.textAlign);
4930
4956
  }
4957
+ auto tmp_fontWeight = map.find("fontWeight");
4958
+ if (tmp_fontWeight != map.end()) {
4959
+ fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
4960
+ }
4931
4961
  }
4932
4962
 
4933
4963
  static inline std::string toString(const EnrichedMarkdownTextMarkdownStyleMathStruct &value) {
@@ -4943,6 +4973,7 @@ static inline folly::dynamic toDynamic(const EnrichedMarkdownTextMarkdownStyleMa
4943
4973
  struct EnrichedMarkdownTextMarkdownStyleInlineMathStruct {
4944
4974
  SharedColor color{};
4945
4975
  Float paddingVertical{0.0};
4976
+ std::string fontWeight{};
4946
4977
 
4947
4978
 
4948
4979
  #ifdef RN_SERIALIZABLE_STATE
@@ -4952,6 +4983,7 @@ struct EnrichedMarkdownTextMarkdownStyleInlineMathStruct {
4952
4983
  folly::dynamic result = folly::dynamic::object();
4953
4984
  result["color"] = ::facebook::react::toDynamic(color);
4954
4985
  result["paddingVertical"] = paddingVertical;
4986
+ result["fontWeight"] = fontWeight;
4955
4987
  return result;
4956
4988
  }
4957
4989
  #endif
@@ -4968,6 +5000,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
4968
5000
  if (tmp_paddingVertical != map.end()) {
4969
5001
  fromRawValue(context, tmp_paddingVertical->second, result.paddingVertical);
4970
5002
  }
5003
+ auto tmp_fontWeight = map.find("fontWeight");
5004
+ if (tmp_fontWeight != map.end()) {
5005
+ fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
5006
+ }
4971
5007
  }
4972
5008
 
4973
5009
  static inline std::string toString(const EnrichedMarkdownTextMarkdownStyleInlineMathStruct &value) {