Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add support for lineHeight
  • Loading branch information
hsource committed Mar 30, 2022
commit 748f86b3aca35a4f836c7d45f216d53f479e7471
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ In both functions, the text to be measured is required, but the rest of the para
- `fontSize`
- `fontWeight`
- `fontStyle`
- `lineHeight`
- `numberOfLines`
- `fontVariant` (iOS)
- `includeFontPadding` (Android)
- `textBreakStrategy` (Android)
- `letterSpacing`
- `allowFontScaling`
- `numberOfLines`
- `width`: Constraint for automatic line-break based on text-break strategy.

In addition, the library includes functions to obtain information about the fonts visible to the App.
Expand Down Expand Up @@ -96,12 +97,13 @@ fontFamily | string | OS dependent | The default is the same applied by
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).
fontSize | number | 14 | The default font size comes from RN.
fontStyle | string | 'normal' | One of "normal" or "italic".
lineHeight | number | (none) | The line height of each line. Defaults to the font size.
numberOfLines | number | (none) | Limit the number of lines the text can render on
fontVariant | array | (none) | _iOS only_
allowFontScaling | boolean | true | To respect the user' setting of large fonts (i.e. use SP units).
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+_
includeFontPadding | boolean | true | Include additional top and bottom padding, to avoid clipping certain characters.<br>_Android only_
textBreakStrategy | string | 'highQuality' | One of 'simple', 'balanced', or 'highQuality'.<br>_Android only, with API 23+_
numberOfLines | number | (none) | Limit the number of lines the text can render on
width | number | MAX_INT | Restrict the width. The resulting height will vary depending on the automatic flow of the text.
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].
lineInfoForLine | number | (none) | If `>=0`, the result will include a [lineInfo](#lineinfo) property with information for the required line number.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import android.text.style.MetricAffectingSpan;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.views.text.CustomLineHeightSpan;
import com.facebook.react.views.text.TextAttributes;

import javax.annotation.Nonnull;

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

if (!Float.isNaN(conf.lineHeight)) {
priority++;
final TextAttributes textAttributes = new TextAttributes();
textAttributes.setLineHeight(conf.lineHeight);
setSpanOperation(text, end, priority, new CustomLineHeightSpan(textAttributes.getEffectiveLineHeight()));
}

return text;
}

Expand Down
8 changes: 6 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ declare module "react-native-text-size" {
fontSize?: number;
fontStyle?: TSFontStyle;
fontWeight?: TSFontWeight;
lineHeight?: number;
/**
* Number of lines to limit the text to. Corresponds to the `numberOfLines`
* prop on `<Text>`
*/
numberOfLines?: number;
/** @platform ios */
fontVariant?: Array<TSFontVariant>;
/** iOS all, Android SDK 21+ with RN 0.55+ */
Expand All @@ -72,8 +78,6 @@ declare module "react-native-text-size" {
includeFontPadding?: boolean;
/** @platform android (SDK 23+) */
textBreakStrategy?: TSTextBreakStrategy;
/** Number of lines to limit the text to */
numberOfLines?: number;
}

export type TSFontForStyle = {
Expand Down
8 changes: 6 additions & 2 deletions index.js.flow
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ export type TSFontSpecs = {
fontSize?: number,
fontStyle?: TSFontStyle,
fontWeight?: TSFontWeight,
lineHeight?: number;
/**
* Number of lines to limit the text to. Corresponds to the `numberOfLines`
* prop on `<Text>`
*/
numberOfLines?: number,
/** @platform ios */
fontVariant?: Array<TSFontVariant>,
/** iOS all, Android SDK 21+ with RN 0.55+ */
Expand All @@ -72,8 +78,6 @@ export type TSFontSpecs = {
includeFontPadding?: boolean,
/** @platform android (SDK 23+) */
textBreakStrategy?: TSTextBreakStrategy,
/** Number of lines to limit the text to */
numberOfLines?: number,
}

export type TSFontForStyle = {
Expand Down
103 changes: 68 additions & 35 deletions ios/RNTextSize.m
Original file line number Diff line number Diff line change
Expand Up @@ -86,25 +86,15 @@ - (dispatch_queue_t)methodQueue {
return;
}

// Allow the user to specify the width or height (both optionals).
const CGFloat optWidth = CGFloatValueFrom(options[@"width"]);
const CGFloat maxWidth = isnan(optWidth) || isinf(optWidth) ? CGFLOAT_MAX : optWidth;
const CGSize maxSize = CGSizeMake(maxWidth, CGFLOAT_MAX);

// Create attributes for the font and the optional letter spacing.
const CGSize maxSize = [self maxSizeFromOptions:options];
const CGFloat letterSpacing = CGFloatValueFrom(options[@"letterSpacing"]);
NSDictionary<NSAttributedStringKey,id> *const attributes = isnan(letterSpacing)
? @{NSFontAttributeName: font}
: @{NSFontAttributeName: font, NSKernAttributeName: @(letterSpacing)};

NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:maxSize];
textContainer.lineFragmentPadding = 0.0;
textContainer.lineBreakMode = NSLineBreakByClipping;

const NSInteger numberOfLines = [RCTConvert NSInteger:options[@"numberOfLines"]];
if (numberOfLines > 0) {
textContainer.maximumNumberOfLines = numberOfLines;
}
NSTextContainer *const textContainer =
[self textContainerFromOptions:options withMaxSize:maxSize];
NSDictionary<NSAttributedStringKey,id> *const attributes =
[self textStorageAttributesFromOptions:options
withFont:font
withLetterSpacing:letterSpacing];

NSLayoutManager *layoutManager = [NSLayoutManager new];
[layoutManager addTextContainer:textContainer];
Expand Down Expand Up @@ -171,24 +161,15 @@ - (dispatch_queue_t)methodQueue {
return;
}

const CGFloat optWidth = CGFloatValueFrom(options[@"width"]);
const CGFloat maxWidth = isnan(optWidth) || isinf(optWidth) ? CGFLOAT_MAX : optWidth;
const CGSize maxSize = CGSizeMake(maxWidth, CGFLOAT_MAX);

// Create attributes for the font and the optional letter spacing.
const CGSize maxSize = [self maxSizeFromOptions:options];
const CGFloat letterSpacing = CGFloatValueFrom(options[@"letterSpacing"]);
NSDictionary<NSAttributedStringKey,id> *const attributes = isnan(letterSpacing)
? @{NSFontAttributeName: font}
: @{NSFontAttributeName: font, NSKernAttributeName: @(letterSpacing)};

NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:maxSize];
textContainer.lineFragmentPadding = 0.0;
textContainer.lineBreakMode = NSLineBreakByClipping;

const NSInteger numberOfLines = [RCTConvert NSInteger:options[@"numberOfLines"]];
if (numberOfLines > 0) {
textContainer.maximumNumberOfLines = numberOfLines;
}
NSTextContainer *const textContainer =
[self textContainerFromOptions:options withMaxSize:maxSize];
NSDictionary<NSAttributedStringKey,id> *const attributes =
[self textStorageAttributesFromOptions:options
withFont:font
withLetterSpacing:letterSpacing];

NSLayoutManager *layoutManager = [NSLayoutManager new];
[layoutManager addTextContainer:textContainer];
Expand Down Expand Up @@ -506,7 +487,7 @@ - (NSDictionary *)fontInfoFromUIFont:(const UIFont *)font
* of the weight in multiples of "100", as expected by RN, or one of the words
* "bold" or "normal" if appropiate.
*
* @param trais NSDictionary with the traits of the font.
* @param traits NSDictionary with the traits of the font.
* @return NSString with the weight of the font.
*/
- (NSString *)fontWeightFromTraits:(const NSDictionary *)traits
Expand All @@ -527,7 +508,7 @@ - (NSString *)fontWeightFromTraits:(const NSDictionary *)traits
/**
* Returns a string with the style found in the trait, either "normal" or "italic".
*
* @param trais NSDictionary with the traits of the font.
* @param traits NSDictionary with the traits of the font.
* @return NSString with the style.
*/
- (NSString *)fontStyleFromTraits:(const NSDictionary *)traits
Expand Down Expand Up @@ -591,4 +572,56 @@ - (NSString *)fontStyleFromTraits:(const NSDictionary *)traits
return count ? [NSArray arrayWithObjects:outArr count:count] : nil;
}

- (CGSize)maxSizeFromOptions:(NSDictionary * _Nullable)options
{
const CGFloat optWidth = CGFloatValueFrom(options[@"width"]);
const CGFloat maxWidth = isnan(optWidth) || isinf(optWidth) ? CGFLOAT_MAX : optWidth;
const CGSize maxSize = CGSizeMake(maxWidth, CGFLOAT_MAX);
return maxSize;
}

/**
* Creates a textContainer with the width and numberOfLines from options.
*/
- (NSTextContainer *)textContainerFromOptions:(NSDictionary * _Nullable)options
withMaxSize:(CGSize)maxSize
{
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:maxSize];
textContainer.lineFragmentPadding = 0.0;
textContainer.lineBreakMode = NSLineBreakByClipping;

const NSInteger numberOfLines = [RCTConvert NSInteger:options[@"numberOfLines"]];
if (numberOfLines > 0) {
textContainer.maximumNumberOfLines = numberOfLines;
}

return textContainer;
}

/**
* Creates attributes that should be passed into the TextStorage based on
* parameters and the options the user passes in.
*/
- (NSDictionary<NSAttributedStringKey,id> *const)textStorageAttributesFromOptions:(NSDictionary * _Nullable)options
withFont:(UIFont *const _Nullable)font
withLetterSpacing:(CGFloat)letterSpacing
{
NSMutableDictionary<NSAttributedStringKey,id> *const attributes = [[NSMutableDictionary alloc] init];
[attributes setObject:font forKey:NSFontAttributeName];

if (!isnan(letterSpacing)) {
[attributes setObject:@(letterSpacing) forKey:NSKernAttributeName];
}

const CGFloat lineHeight = CGFloatValueFrom(options[@"lineHeight"]);
if (!isnan(lineHeight)) {
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
[style setMinimumLineHeight:lineHeight];
[style setMaximumLineHeight:lineHeight];
[attributes setObject:style forKey:NSParagraphStyleAttributeName];
}

return attributes;
}

@end