Skip to content

Commit a4e227e

Browse files
authored
[Fabric] Implement minimumFontScale in Text (#14617)
1 parent d2bf5ea commit a4e227e

File tree

4 files changed

+58
-30
lines changed

4 files changed

+58
-30
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "[Fabric] Implement minimumFontScale in Text",
4+
"packageName": "react-native-windows",
5+
"email": "54227869+anupriya13@users.noreply.github.com",
6+
"dependentChangeType": "patch"
7+
}

packages/playground/Samples/text.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ export default class Bootstrap extends React.Component {
2121
Click here : This is a text with a tooltip.
2222
</Text>
2323
<View style={styles.container2}>
24-
<Text adjustsFontSizeToFit style={{maxHeight: 80, fontSize: 72}}>
24+
<Text
25+
adjustsFontSizeToFit
26+
style={{maxHeight: 80, fontSize: 72}}
27+
minimumFontScale={0.5}>
2528
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
2629
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
2730
ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut

vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/TextLayoutManager.cpp

+45-28
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,14 @@ void TextLayoutManager::GetTextLayout(
231231

232232
TextMeasurement::Attachments attachments;
233233
if (paragraphAttributes.adjustsFontSizeToFit) {
234+
auto minimumFontScale = 0.01f;
235+
// Uncomment below part when minimumFontScale is available in ParagraphAttributes
236+
// if (paragraphAttributes.minimumFontScale && paragraphAttributes.minimumFontScale >= 0.01f &&
237+
// paragraphAttributes.minimumFontScale <= 1.0f) {
238+
// minimumFontScale = paragraphAttributes.minimumFontScale;
239+
//}
234240
GetTextLayoutByAdjustingFontSizeToFit(
235-
attributedStringBox, paragraphAttributes, layoutConstraints, spTextLayout, attachments);
241+
attributedStringBox, paragraphAttributes, layoutConstraints, spTextLayout, attachments, minimumFontScale);
236242
} else {
237243
GetTextLayout(attributedStringBox, paragraphAttributes, layoutConstraints.maximumSize, spTextLayout, attachments);
238244
}
@@ -243,52 +249,63 @@ void TextLayoutManager::GetTextLayoutByAdjustingFontSizeToFit(
243249
const ParagraphAttributes &paragraphAttributes,
244250
LayoutConstraints layoutConstraints,
245251
winrt::com_ptr<IDWriteTextLayout> &spTextLayout,
246-
TextMeasurement::Attachments &attachments) noexcept {
252+
TextMeasurement::Attachments &attachments,
253+
float minimumFontScale) noexcept {
247254
/* This function constructs a text layout from the given parameters.
248255
If the generated text layout doesn't fit within the given layout constraints,
249256
it will reduce the font size and construct a new text layout. This process will
250257
be repeated until the text layout meets the constraints.*/
251258

252259
DWRITE_TEXT_METRICS metrics;
253260

254-
// Better Approach should be implemented , this uses O(n)
255-
do {
256-
if (spTextLayout) // Font Size reduction
257-
{
258-
constexpr auto fontReduceFactor = 1.0f;
259-
260-
auto attributedStringToResize = attributedStringBox.getValue();
261+
// Better Approach should be implemented, this uses O(n)
262+
constexpr auto fontReduceFactor = 1.0f;
263+
auto attributedStringToResize = attributedStringBox.getValue();
264+
auto fragmentsCopyToResize = attributedStringToResize.getFragments();
265+
if (fragmentsCopyToResize.empty()) {
266+
return; // No fragments to process
267+
}
261268

262-
auto fragmentsCopyToResize = attributedStringToResize.getFragments();
269+
float initialFontSize = fragmentsCopyToResize[0].textAttributes.fontSize;
270+
float currentFontSize = initialFontSize;
263271

264-
attributedStringToResize.getFragments().clear();
272+
// Calculate the minimum font size as per Android/IOS
273+
float minimumFontSize = std::max(minimumFontScale * initialFontSize, 4.0f);
265274

266-
for (auto fragment : fragmentsCopyToResize) {
267-
fragment.textAttributes.fontSize -= fontReduceFactor;
275+
// Initial measurement
276+
GetTextLayout(attributedStringBox, paragraphAttributes, layoutConstraints.maximumSize, spTextLayout, attachments);
277+
if (spTextLayout) {
278+
winrt::check_hresult(spTextLayout->GetMetrics(&metrics));
279+
} else {
280+
return;
281+
}
268282

269-
attributedStringToResize.appendFragment(std::move(fragment));
270-
}
271-
attributedStringBox = facebook::react::AttributedStringBox(attributedStringToResize);
283+
// Loop until the font size is reduced to the minimum or the layout fits
284+
while ((currentFontSize > minimumFontSize) &&
285+
((paragraphAttributes.maximumNumberOfLines != 0 &&
286+
paragraphAttributes.maximumNumberOfLines < static_cast<int>(metrics.lineCount)) ||
287+
metrics.height > metrics.layoutHeight || metrics.width > metrics.layoutWidth)) {
288+
// Reduce the font size by 1 point (or a configurable factor)
289+
currentFontSize = std::max(currentFontSize - fontReduceFactor, minimumFontSize);
290+
291+
// Adjust font size for all fragments proportionally
292+
attributedStringToResize.getFragments().clear();
293+
for (auto fragment : fragmentsCopyToResize) {
294+
fragment.textAttributes.fontSize =
295+
std::max(fragment.textAttributes.fontSize * (currentFontSize / initialFontSize), minimumFontSize);
296+
attributedStringToResize.appendFragment(std::move(fragment));
272297
}
273298

274-
GetTextLayout(attributedStringBox, paragraphAttributes, layoutConstraints.maximumSize, spTextLayout, attachments);
299+
attributedStringBox = facebook::react::AttributedStringBox(attributedStringToResize);
275300

301+
// Re-measure the text layout
302+
GetTextLayout(attributedStringBox, paragraphAttributes, layoutConstraints.maximumSize, spTextLayout, attachments);
276303
if (spTextLayout) {
277-
const auto defaultMinFontSize = 2.0f;
278-
279-
// TODO : changes for minimumFontScale prop can be added.
280-
281-
if (spTextLayout->GetFontSize() <= defaultMinFontSize) {
282-
break; // reached minimum font size , so no more size reducing
283-
}
284304
winrt::check_hresult(spTextLayout->GetMetrics(&metrics));
285305
} else {
286306
return;
287307
}
288-
289-
} while ((paragraphAttributes.maximumNumberOfLines != 0 &&
290-
paragraphAttributes.maximumNumberOfLines < static_cast<int>(metrics.lineCount)) ||
291-
metrics.height > metrics.layoutHeight || metrics.width > metrics.layoutWidth);
308+
}
292309
}
293310

294311
// measure entire text (inluding attachments)

vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/TextLayoutManager.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ class TextLayoutManager {
9393
const ParagraphAttributes &paragraphAttributes,
9494
LayoutConstraints layoutConstraints,
9595
winrt::com_ptr<IDWriteTextLayout> &spTextLayout,
96-
TextMeasurement::Attachments &attachments) noexcept;
96+
TextMeasurement::Attachments &attachments,
97+
float minimumFontScale) noexcept;
9798

9899
#pragma endregion
99100

0 commit comments

Comments
 (0)