@@ -68,10 +68,6 @@ - (NSDictionary *)constantsToExport {
6868 // iOS standard sizes
6969 NSDictionary *fontSize = @{
7070 @" default" : @(14 ),
71- @" button" : @([UIFont buttonFontSize ]), // 18
72- @" label" : @([UIFont labelFontSize ]), // 17
73- @" smallSystem" : @([UIFont smallSystemFontSize ]), // 12
74- @" system" : @([UIFont systemFontSize ]), // 14
7571 };
7672 return @{@" FontSize" : fontSize};
7773}
@@ -168,6 +164,72 @@ - (NSDictionary *)constantsToExport {
168164 resolve (result);
169165}
170166
167+ /* *
168+ * Gets the width, height, line count and last line width for the provided text
169+ * font specifications.
170+ * Based on `RCTTextShadowViewMeasure` of Libraries/Text/Text/RCTTextShadowView.m
171+ */
172+ RCT_EXPORT_METHOD (flatHeights:(NSDictionary * _Nullable)options
173+ resolver:(RCTPromiseResolveBlock)resolve
174+ rejecter:(RCTPromiseRejectBlock)reject)
175+ {
176+ NSArray <NSString *> *const _Nullable texts = [RCTConvert NSStringArray: options[@" text" ]];
177+ if (isNull (texts)) {
178+ reject (E_MISSING_TEXT, @" Missing required text, must be an array." , nil );
179+ return ;
180+ }
181+
182+ UIFont *const _Nullable font = [RNTextSize UIFontFromUserSpecs: options withBridge: _bridge];
183+ if (!font) {
184+ reject (E_INVALID_FONT_SPEC, @" Invalid font specification." , nil );
185+ return ;
186+ }
187+
188+ const CGFloat optWidth = CGFloatValueFrom (options[@" width" ]);
189+ const CGFloat maxWidth = isnan (optWidth) || isinf (optWidth) ? CGFLOAT_MAX : optWidth;
190+ const CGSize maxSize = CGSizeMake (maxWidth, CGFLOAT_MAX);
191+
192+ // Create attributes for the font and the optional letter spacing.
193+ const CGFloat letterSpacing = CGFloatValueFrom (options[@" letterSpacing" ]);
194+ NSDictionary <NSAttributedStringKey ,id > *const attributes = isnan (letterSpacing)
195+ ? @{NSFontAttributeName : font}
196+ : @{NSFontAttributeName : font, NSKernAttributeName : @(letterSpacing)};
197+
198+ NSTextContainer *textContainer = [[NSTextContainer alloc ] initWithSize: maxSize];
199+ textContainer.lineFragmentPadding = 0.0 ;
200+ textContainer.lineBreakMode = NSLineBreakByClipping; // no maxlines support
201+
202+ NSLayoutManager *layoutManager = [NSLayoutManager new ];
203+ [layoutManager addTextContainer: textContainer];
204+
205+ NSMutableArray <NSNumber *> *result = [[NSMutableArray alloc ] initWithCapacity: texts.count];
206+ const CGFloat epsilon = 1 / RCTScreenScale (); // Yoga seems do this
207+
208+ for (int ix = 0 ; ix < texts.count ; ix++) {
209+ NSString *text = texts[ix];
210+ if (!text) {
211+ result[ix] = @0 ;
212+ continue ;
213+ }
214+ if (text == (id ) kCFNull ) {
215+ result[ix] = @0 ;
216+ continue ;
217+ }
218+
219+ NSTextStorage *textStorage = [[NSTextStorage alloc ] initWithString: text attributes: attributes];
220+ [textStorage addLayoutManager: layoutManager];
221+
222+ [layoutManager ensureLayoutForTextContainer: textContainer];
223+ CGSize size = [layoutManager usedRectForTextContainer: textContainer].size ;
224+ [textStorage removeLayoutManager: layoutManager];
225+
226+ const CGFloat height = MIN (RCTCeilPixelValue (size.height + epsilon), maxSize.height );
227+ result[ix] = @(height);
228+ }
229+
230+ resolve (result);
231+ }
232+
171233/* *
172234 * Resolve with an object with info about a font built with the parameters provided by
173235 * the user. Rejects if the parameters are falsy or the font could not be created.
0 commit comments