diff --git a/android/src/main/java/com/expensify/livemarkdown/MarkdownStyle.java b/android/src/main/java/com/expensify/livemarkdown/MarkdownStyle.java index cf691e62..88bbbf1b 100644 --- a/android/src/main/java/com/expensify/livemarkdown/MarkdownStyle.java +++ b/android/src/main/java/com/expensify/livemarkdown/MarkdownStyle.java @@ -21,6 +21,8 @@ public class MarkdownStyle { private final float mH1FontSize; + private final float mH1LineHeight; + private final float mEmojiFontSize; @ColorInt @@ -74,6 +76,7 @@ public MarkdownStyle(@NonNull ReadableMap map, @NonNull Context context) { mSyntaxColor = parseColor(map, "syntax", "color", context); mLinkColor = parseColor(map, "link", "color", context); mH1FontSize = parseFloat(map, "h1", "fontSize"); + mH1LineHeight = parseFloat(map, "h1", "lineHeight"); mEmojiFontSize = parseFloat(map, "emoji", "fontSize"); mBlockquoteBorderColor = parseColor(map, "blockquote", "borderColor", context); mBlockquoteBorderWidth = parseFloat(map, "blockquote", "borderWidth"); @@ -136,6 +139,10 @@ public float getH1FontSize() { return mH1FontSize; } + public float getH1LineHeight() { + return mH1LineHeight; + } + public float getEmojiFontSize() { return mEmojiFontSize; } diff --git a/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java b/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java index e51aa4b4..e95c131a 100644 --- a/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java +++ b/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java @@ -148,10 +148,10 @@ private void applyRange(SpannableStringBuilder ssb, String type, int start, int break; case "h1": setSpan(ssb, new MarkdownBoldSpan(), start, end); - CustomLineHeightSpan[] spans = ssb.getSpans(0, ssb.length(), CustomLineHeightSpan.class); - if (spans.length >= 1) { - int lineHeight = spans[0].getLineHeight(); - setSpan(ssb, new MarkdownLineHeightSpan(lineHeight * 1.5f), start, end); + float lineHeight = mMarkdownStyle.getH1LineHeight(); + if (lineHeight != -1) { + // NOTE: actually, we should also include "# " but it also works this way + setSpan(ssb, new MarkdownLineHeightSpan(lineHeight), start, end); } // NOTE: size span must be set after line height span to avoid height jumps setSpan(ssb, new MarkdownFontSizeSpan(mMarkdownStyle.getH1FontSize()), start, end); diff --git a/android/src/main/java/com/expensify/livemarkdown/spans/MarkdownLineHeightSpan.java b/android/src/main/java/com/expensify/livemarkdown/spans/MarkdownLineHeightSpan.java index 3f4c33fc..936020e7 100644 --- a/android/src/main/java/com/expensify/livemarkdown/spans/MarkdownLineHeightSpan.java +++ b/android/src/main/java/com/expensify/livemarkdown/spans/MarkdownLineHeightSpan.java @@ -3,16 +3,19 @@ import android.graphics.Paint; import android.text.style.LineHeightSpan; +import com.facebook.react.uimanager.PixelUtil; +import com.facebook.react.views.text.internal.span.CustomLineHeightSpan; + public class MarkdownLineHeightSpan implements MarkdownSpan, LineHeightSpan { - private final float mLineHeight; + private final CustomLineHeightSpan mCustomLineHeightSpan; public MarkdownLineHeightSpan(float lineHeight) { - mLineHeight = lineHeight; + mCustomLineHeightSpan = new CustomLineHeightSpan(PixelUtil.toPixelFromDIP(lineHeight)); } @Override public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int lineHeight, Paint.FontMetricsInt fm) { - fm.top -= mLineHeight / 4; - fm.ascent -= mLineHeight / 4; + // CustomLineHeightSpan is marked as final, we can't extend it, but we can use it via this adapter + mCustomLineHeightSpan.chooseHeight(text, start, end, spanstartv, lineHeight, fm); } } diff --git a/apple/RCTMarkdownStyle.h b/apple/RCTMarkdownStyle.h index 75a27f16..0c1c1edc 100644 --- a/apple/RCTMarkdownStyle.h +++ b/apple/RCTMarkdownStyle.h @@ -11,6 +11,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) UIColor *syntaxColor; @property (nonatomic) UIColor *linkColor; @property (nonatomic) CGFloat h1FontSize; +@property (nonatomic) CGFloat h1LineHeight; @property (nonatomic) CGFloat emojiFontSize; @property (nonatomic) UIColor *blockquoteBorderColor; @property (nonatomic) CGFloat blockquoteBorderWidth; diff --git a/apple/RCTMarkdownStyle.mm b/apple/RCTMarkdownStyle.mm index a2752cdf..1a031a0e 100644 --- a/apple/RCTMarkdownStyle.mm +++ b/apple/RCTMarkdownStyle.mm @@ -18,6 +18,7 @@ - (instancetype)initWithStruct:(const facebook::react::MarkdownTextInputDecorato _linkColor = RCTUIColorFromSharedColor(style.link.color); _h1FontSize = style.h1.fontSize; + _h1LineHeight = style.h1.lineHeight; _emojiFontSize = style.emoji.fontSize; @@ -59,6 +60,7 @@ - (instancetype)initWithDictionary:(NSDictionary *)json _linkColor = [RCTConvert UIColor:json[@"link"][@"color"]]; _h1FontSize = [RCTConvert CGFloat:json[@"h1"][@"fontSize"]]; + _h1LineHeight = [RCTConvert CGFloat:json[@"h1"][@"lineHeight"]]; _emojiFontSize = [RCTConvert CGFloat:json[@"emoji"][@"fontSize"]]; diff --git a/apple/RCTMarkdownUtils.mm b/apple/RCTMarkdownUtils.mm index c22a8784..ff7849a5 100644 --- a/apple/RCTMarkdownUtils.mm +++ b/apple/RCTMarkdownUtils.mm @@ -159,6 +159,11 @@ - (void)applyRangeToAttributedString:(NSMutableAttributedString *)attributedStri @"range": [NSValue valueWithRange:range], @"depth": @(depth) }]; + } else if (type == "h1" && _markdownStyle.h1LineHeight != -1) { + NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new]; + paragraphStyle.lineHeightMultiple = _markdownStyle.h1LineHeight / _markdownStyle.h1FontSize; + NSRange rangeWithHashAndSpace = NSMakeRange(range.location - 2, range.length + 2); + [attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:rangeWithHashAndSpace]; } else if (type == "pre") { [attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.preColor range:range]; NSRange rangeForBackground = [[attributedString string] characterAtIndex:range.location] == '\n' ? NSMakeRange(range.location + 1, range.length - 1) : range; diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 14c774cb..df65eab1 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1497,7 +1497,7 @@ PODS: - React-logger (= 0.75.2) - React-perflogger (= 0.75.2) - React-utils (= 0.75.2) - - RNLiveMarkdown (0.1.169): + - RNLiveMarkdown (0.1.180): - DoubleConversion - glog - hermes-engine @@ -1517,9 +1517,9 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNLiveMarkdown/newarch (= 0.1.169) + - RNLiveMarkdown/newarch (= 0.1.180) - Yoga - - RNLiveMarkdown/newarch (0.1.169): + - RNLiveMarkdown/newarch (0.1.180): - DoubleConversion - glog - hermes-engine @@ -1805,7 +1805,7 @@ SPEC CHECKSUMS: React-utils: 81a715d9c0a2a49047e77a86f3a2247408540deb ReactCodegen: 60973d382704c793c605b9be0fc7f31cb279442f ReactCommon: 6ef348087d250257c44c0204461c03f036650e9b - RNLiveMarkdown: 00ab78496be2ae15a15a83f14ba732c01624f02c + RNLiveMarkdown: fc07b203a3ed832e2e5d3950e69cd4fc3b0568b6 SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d Yoga: 2a45d7e59592db061217551fd3bbe2dd993817ae diff --git a/example/src/App.tsx b/example/src/App.tsx index 235ff535..13c17675 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -27,6 +27,9 @@ export default function App() { link: { color: linkColorState ? 'red' : 'blue', }, + h1: { + lineHeight: 50, + }, }; }, [emojiFontSizeState, linkColorState]); diff --git a/src/MarkdownTextInputDecoratorViewNativeComponent.ts b/src/MarkdownTextInputDecoratorViewNativeComponent.ts index 7617963d..14fa57ac 100644 --- a/src/MarkdownTextInputDecoratorViewNativeComponent.ts +++ b/src/MarkdownTextInputDecoratorViewNativeComponent.ts @@ -15,6 +15,7 @@ interface MarkdownStyle { }; h1: { fontSize: Float; + lineHeight: Float; }; blockquote: { borderColor: ColorValue; diff --git a/src/styleUtils.ts b/src/styleUtils.ts index 47cfa529..bb2e6343 100644 --- a/src/styleUtils.ts +++ b/src/styleUtils.ts @@ -20,6 +20,7 @@ function makeDefaultMarkdownStyle(): MarkdownStyle { }, h1: { fontSize: 25, + lineHeight: -1, }, emoji: { fontSize: 20,