-
Notifications
You must be signed in to change notification settings - Fork 25.2k
fix: never round a text node below its measured width #58270
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
+103
−9
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
72 changes: 72 additions & 0 deletions
packages/react-native/Libraries/Text/__tests__/Text-pixelGridRounding-itest.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| /** | ||
| * Copyright (c) Meta Platforms, Inc. and affiliates. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE file in the root directory of this source tree. | ||
| * | ||
| * @flow strict-local | ||
| * @format | ||
| */ | ||
|
|
||
| import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment'; | ||
|
|
||
| import type {HostInstance} from 'react-native'; | ||
|
|
||
| import * as Fantom from '@react-native/fantom'; | ||
| import nullthrows from 'nullthrows'; | ||
| import * as React from 'react'; | ||
| import {createRef} from 'react'; | ||
| import {Text, View} from 'react-native'; | ||
|
|
||
| // A non-integer screen density, as shipped by many Android devices. One | ||
| // physical pixel is 1/2.75 dp. | ||
| const DEVICE_PIXEL_RATIO = 2.75; | ||
|
|
||
| // 23 physical pixels, as a float32 dp value. Android measures text in whole | ||
| // physical pixels and hands Yoga the dp equivalent, so `TEXT_WIDTH * 2.75` | ||
| // lands a hair under 23 (22.999999046) rather than exactly on it. | ||
| const TEXT_WIDTH = 8.363636016845703; | ||
| const TEXT_WIDTH_IN_PIXELS = 23; | ||
|
|
||
| function measureTextWidthInPixels(paddingLeft: number): number { | ||
| const root = Fantom.createRoot({devicePixelRatio: DEVICE_PIXEL_RATIO}); | ||
| const textRef = createRef<HostInstance>(); | ||
|
|
||
| try { | ||
| Fantom.runTask(() => { | ||
| root.render( | ||
| <View collapsable={false} style={{paddingLeft}}> | ||
| <Text ref={textRef} style={{width: TEXT_WIDTH}}> | ||
| text | ||
| </Text> | ||
| </View>, | ||
| ); | ||
| }); | ||
|
|
||
| const {width} = nullthrows(textRef.current).getBoundingClientRect(); | ||
| return Math.round(width * DEVICE_PIXEL_RATIO); | ||
| } finally { | ||
| root.destroy(); | ||
| } | ||
| } | ||
|
|
||
| // The text node's edges land on opposite sides of the tolerance Yoga uses to | ||
| // decide whether a value already sits on the pixel grid: the left edge falls on | ||
| // 103.9999008 scaled units and is snapped up to 104, while the right edge falls | ||
| // on 126.9998999, misses the tolerance, and is floored to 126. Rounding the two | ||
| // edges independently then produced a 22 pixel wide box for 23 pixels of text, | ||
| // and the view dropped the trailing glyph when it re-broke the text at that | ||
| // width. | ||
| test('does not round a text node below the width it measured', () => { | ||
| expect(measureTextWidthInPixels(37.818145751953125)).toBe( | ||
| TEXT_WIDTH_IN_PIXELS, | ||
| ); | ||
| }); | ||
|
|
||
| // The counterpart: a text node that already rounds to the width it measured | ||
| // must not gain a pixel. Here the left edge falls on 28.875 scaled units, well | ||
| // clear of the tolerance, and both edges floor consistently. Forcing the right | ||
| // edge to ceil unconditionally would widen this node to 24 pixels. | ||
| test('does not widen a text node that already fits', () => { | ||
| expect(measureTextWidthInPixels(10.5)).toBe(TEXT_WIDTH_IN_PIXELS); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.