Skip to content

Commit 748f86b

Browse files
committed
Add support for lineHeight
1 parent 4472a07 commit 748f86b

File tree

5 files changed

+93
-41
lines changed

5 files changed

+93
-41
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,13 @@ In both functions, the text to be measured is required, but the rest of the para
2323
- `fontSize`
2424
- `fontWeight`
2525
- `fontStyle`
26+
- `lineHeight`
27+
- `numberOfLines`
2628
- `fontVariant` (iOS)
2729
- `includeFontPadding` (Android)
2830
- `textBreakStrategy` (Android)
2931
- `letterSpacing`
3032
- `allowFontScaling`
31-
- `numberOfLines`
3233
- `width`: Constraint for automatic line-break based on text-break strategy.
3334

3435
In addition, the library includes functions to obtain information about the fonts visible to the App.
@@ -96,12 +97,13 @@ fontFamily | string | OS dependent | The default is the same applied by
9697
fontWeight | string | 'normal' | On android, numeric ranges has no granularity and '500' to '900' becomes 'bold', but you can use a `fontFamily` of specific weight ("sans-serif-thin", "sans-serif-medium", etc).
9798
fontSize | number | 14 | The default font size comes from RN.
9899
fontStyle | string | 'normal' | One of "normal" or "italic".
100+
lineHeight | number | (none) | The line height of each line. Defaults to the font size.
101+
numberOfLines | number | (none) | Limit the number of lines the text can render on
99102
fontVariant | array | (none) | _iOS only_
100103
allowFontScaling | boolean | true | To respect the user' setting of large fonts (i.e. use SP units).
101104
letterSpacing | number | (none) | Additional spacing between characters (aka `tracking`).<br>**Note:** In iOS a zero cancels automatic kerning.<br>_All iOS, Android with API 21+_
102105
includeFontPadding | boolean | true | Include additional top and bottom padding, to avoid clipping certain characters.<br>_Android only_
103106
textBreakStrategy | string | 'highQuality' | One of 'simple', 'balanced', or 'highQuality'.<br>_Android only, with API 23+_
104-
numberOfLines | number | (none) | Limit the number of lines the text can render on
105107
width | number | MAX_INT | Restrict the width. The resulting height will vary depending on the automatic flow of the text.
106108
usePreciseWidth | boolean | false | If `true`, the result will include an exact `width` and the `lastLineWidth` property.<br>You can see the effect of this flag in the [sample App][sample-app].
107109
lineInfoForLine | number | (none) | If `>=0`, the result will include a [lineInfo](#lineinfo) property with information for the required line number.

android/src/main/java/com/github/amarcruz/rntextsize/RNTextSizeSpannedText.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import android.text.style.MetricAffectingSpan;
1010

1111
import com.facebook.react.bridge.ReactApplicationContext;
12+
import com.facebook.react.views.text.CustomLineHeightSpan;
13+
import com.facebook.react.views.text.TextAttributes;
1214

1315
import javax.annotation.Nonnull;
1416

@@ -49,6 +51,13 @@ static Spannable spannedFromSpecsAndText(
4951
new CustomStyleSpan(RNTextSizeConf.getFont(context, conf.fontFamily, conf.fontStyle)));
5052
}
5153

54+
if (!Float.isNaN(conf.lineHeight)) {
55+
priority++;
56+
final TextAttributes textAttributes = new TextAttributes();
57+
textAttributes.setLineHeight(conf.lineHeight);
58+
setSpanOperation(text, end, priority, new CustomLineHeightSpan(textAttributes.getEffectiveLineHeight()));
59+
}
60+
5261
return text;
5362
}
5463

index.d.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ declare module "react-native-text-size" {
6464
fontSize?: number;
6565
fontStyle?: TSFontStyle;
6666
fontWeight?: TSFontWeight;
67+
lineHeight?: number;
68+
/**
69+
* Number of lines to limit the text to. Corresponds to the `numberOfLines`
70+
* prop on `<Text>`
71+
*/
72+
numberOfLines?: number;
6773
/** @platform ios */
6874
fontVariant?: Array<TSFontVariant>;
6975
/** iOS all, Android SDK 21+ with RN 0.55+ */
@@ -72,8 +78,6 @@ declare module "react-native-text-size" {
7278
includeFontPadding?: boolean;
7379
/** @platform android (SDK 23+) */
7480
textBreakStrategy?: TSTextBreakStrategy;
75-
/** Number of lines to limit the text to */
76-
numberOfLines?: number;
7781
}
7882

7983
export type TSFontForStyle = {

index.js.flow

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ export type TSFontSpecs = {
6464
fontSize?: number,
6565
fontStyle?: TSFontStyle,
6666
fontWeight?: TSFontWeight,
67+
lineHeight?: number;
68+
/**
69+
* Number of lines to limit the text to. Corresponds to the `numberOfLines`
70+
* prop on `<Text>`
71+
*/
72+
numberOfLines?: number,
6773
/** @platform ios */
6874
fontVariant?: Array<TSFontVariant>,
6975
/** iOS all, Android SDK 21+ with RN 0.55+ */
@@ -72,8 +78,6 @@ export type TSFontSpecs = {
7278
includeFontPadding?: boolean,
7379
/** @platform android (SDK 23+) */
7480
textBreakStrategy?: TSTextBreakStrategy,
75-
/** Number of lines to limit the text to */
76-
numberOfLines?: number,
7781
}
7882

7983
export type TSFontForStyle = {

ios/RNTextSize.m

Lines changed: 68 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -86,25 +86,15 @@ - (dispatch_queue_t)methodQueue {
8686
return;
8787
}
8888

89-
// Allow the user to specify the width or height (both optionals).
90-
const CGFloat optWidth = CGFloatValueFrom(options[@"width"]);
91-
const CGFloat maxWidth = isnan(optWidth) || isinf(optWidth) ? CGFLOAT_MAX : optWidth;
92-
const CGSize maxSize = CGSizeMake(maxWidth, CGFLOAT_MAX);
93-
94-
// Create attributes for the font and the optional letter spacing.
89+
const CGSize maxSize = [self maxSizeFromOptions:options];
9590
const CGFloat letterSpacing = CGFloatValueFrom(options[@"letterSpacing"]);
96-
NSDictionary<NSAttributedStringKey,id> *const attributes = isnan(letterSpacing)
97-
? @{NSFontAttributeName: font}
98-
: @{NSFontAttributeName: font, NSKernAttributeName: @(letterSpacing)};
99-
100-
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:maxSize];
101-
textContainer.lineFragmentPadding = 0.0;
102-
textContainer.lineBreakMode = NSLineBreakByClipping;
10391

104-
const NSInteger numberOfLines = [RCTConvert NSInteger:options[@"numberOfLines"]];
105-
if (numberOfLines > 0) {
106-
textContainer.maximumNumberOfLines = numberOfLines;
107-
}
92+
NSTextContainer *const textContainer =
93+
[self textContainerFromOptions:options withMaxSize:maxSize];
94+
NSDictionary<NSAttributedStringKey,id> *const attributes =
95+
[self textStorageAttributesFromOptions:options
96+
withFont:font
97+
withLetterSpacing:letterSpacing];
10898

10999
NSLayoutManager *layoutManager = [NSLayoutManager new];
110100
[layoutManager addTextContainer:textContainer];
@@ -171,24 +161,15 @@ - (dispatch_queue_t)methodQueue {
171161
return;
172162
}
173163

174-
const CGFloat optWidth = CGFloatValueFrom(options[@"width"]);
175-
const CGFloat maxWidth = isnan(optWidth) || isinf(optWidth) ? CGFLOAT_MAX : optWidth;
176-
const CGSize maxSize = CGSizeMake(maxWidth, CGFLOAT_MAX);
177-
178-
// Create attributes for the font and the optional letter spacing.
164+
const CGSize maxSize = [self maxSizeFromOptions:options];
179165
const CGFloat letterSpacing = CGFloatValueFrom(options[@"letterSpacing"]);
180-
NSDictionary<NSAttributedStringKey,id> *const attributes = isnan(letterSpacing)
181-
? @{NSFontAttributeName: font}
182-
: @{NSFontAttributeName: font, NSKernAttributeName: @(letterSpacing)};
183166

184-
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:maxSize];
185-
textContainer.lineFragmentPadding = 0.0;
186-
textContainer.lineBreakMode = NSLineBreakByClipping;
187-
188-
const NSInteger numberOfLines = [RCTConvert NSInteger:options[@"numberOfLines"]];
189-
if (numberOfLines > 0) {
190-
textContainer.maximumNumberOfLines = numberOfLines;
191-
}
167+
NSTextContainer *const textContainer =
168+
[self textContainerFromOptions:options withMaxSize:maxSize];
169+
NSDictionary<NSAttributedStringKey,id> *const attributes =
170+
[self textStorageAttributesFromOptions:options
171+
withFont:font
172+
withLetterSpacing:letterSpacing];
192173

193174
NSLayoutManager *layoutManager = [NSLayoutManager new];
194175
[layoutManager addTextContainer:textContainer];
@@ -506,7 +487,7 @@ - (NSDictionary *)fontInfoFromUIFont:(const UIFont *)font
506487
* of the weight in multiples of "100", as expected by RN, or one of the words
507488
* "bold" or "normal" if appropiate.
508489
*
509-
* @param trais NSDictionary with the traits of the font.
490+
* @param traits NSDictionary with the traits of the font.
510491
* @return NSString with the weight of the font.
511492
*/
512493
- (NSString *)fontWeightFromTraits:(const NSDictionary *)traits
@@ -527,7 +508,7 @@ - (NSString *)fontWeightFromTraits:(const NSDictionary *)traits
527508
/**
528509
* Returns a string with the style found in the trait, either "normal" or "italic".
529510
*
530-
* @param trais NSDictionary with the traits of the font.
511+
* @param traits NSDictionary with the traits of the font.
531512
* @return NSString with the style.
532513
*/
533514
- (NSString *)fontStyleFromTraits:(const NSDictionary *)traits
@@ -591,4 +572,56 @@ - (NSString *)fontStyleFromTraits:(const NSDictionary *)traits
591572
return count ? [NSArray arrayWithObjects:outArr count:count] : nil;
592573
}
593574

575+
- (CGSize)maxSizeFromOptions:(NSDictionary * _Nullable)options
576+
{
577+
const CGFloat optWidth = CGFloatValueFrom(options[@"width"]);
578+
const CGFloat maxWidth = isnan(optWidth) || isinf(optWidth) ? CGFLOAT_MAX : optWidth;
579+
const CGSize maxSize = CGSizeMake(maxWidth, CGFLOAT_MAX);
580+
return maxSize;
581+
}
582+
583+
/**
584+
* Creates a textContainer with the width and numberOfLines from options.
585+
*/
586+
- (NSTextContainer *)textContainerFromOptions:(NSDictionary * _Nullable)options
587+
withMaxSize:(CGSize)maxSize
588+
{
589+
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:maxSize];
590+
textContainer.lineFragmentPadding = 0.0;
591+
textContainer.lineBreakMode = NSLineBreakByClipping;
592+
593+
const NSInteger numberOfLines = [RCTConvert NSInteger:options[@"numberOfLines"]];
594+
if (numberOfLines > 0) {
595+
textContainer.maximumNumberOfLines = numberOfLines;
596+
}
597+
598+
return textContainer;
599+
}
600+
601+
/**
602+
* Creates attributes that should be passed into the TextStorage based on
603+
* parameters and the options the user passes in.
604+
*/
605+
- (NSDictionary<NSAttributedStringKey,id> *const)textStorageAttributesFromOptions:(NSDictionary * _Nullable)options
606+
withFont:(UIFont *const _Nullable)font
607+
withLetterSpacing:(CGFloat)letterSpacing
608+
{
609+
NSMutableDictionary<NSAttributedStringKey,id> *const attributes = [[NSMutableDictionary alloc] init];
610+
[attributes setObject:font forKey:NSFontAttributeName];
611+
612+
if (!isnan(letterSpacing)) {
613+
[attributes setObject:@(letterSpacing) forKey:NSKernAttributeName];
614+
}
615+
616+
const CGFloat lineHeight = CGFloatValueFrom(options[@"lineHeight"]);
617+
if (!isnan(lineHeight)) {
618+
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
619+
[style setMinimumLineHeight:lineHeight];
620+
[style setMaximumLineHeight:lineHeight];
621+
[attributes setObject:style forKey:NSParagraphStyleAttributeName];
622+
}
623+
624+
return attributes;
625+
}
626+
594627
@end

0 commit comments

Comments
 (0)