Skip to content

Commit f201dee

Browse files
vchelaruclaude
andauthored
Fix raylib Text wrapping to measure inline BBCode run sizes (#3532) (#3533)
* Fix raylib Text wrapping to measure inline BBCode run sizes (#3532) raylib's Text wrapped inline BBCode runs at the base font size: a fixed-width Text with an enlarged [FontSize=N]/[FontScale=N] run packed words onto a base-measured line, so the enlarged run spilled past the wrap width. This is the raylib mirror of the XNA-family fix #3529. Two-part fix (mirroring #3529): (1) override IWrappedText.MeasureString(string, int absoluteStartIndexInStrippedText) on the raylib Text so the shared wrap loop measures each styled run at its own resolved size, extracting the shared MeasureStyledLineInBaseUnits helper from GetInlineVariableAwareWidthAndHeight so draw/size/wrap use one per-run resolution; (2) re-wrap after InlineVariables populate in raylib's CustomSetPropertyOnRenderable (make Text.UpdateWrappedText public), since the first wrap runs when RawText is set, before the runs exist. Pins in RaylibGum.Tests: red-first end-to-end wrap tests for a [FontScale=2] run and a [FontSize=N] crisp-swap run (real font measurement, wrap width derived between plain and enlarged single-line widths), plus a fallback pin that MeasureString(string,int) equals the base measure when no inline runs cover the range (plain-text wrapping unchanged). Closes #3532 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * raylib Text: advance wrapped lines by per-line height so an enlarged run doesn't overlap the next line (#3532) The wrap-width fix made enlarged inline runs wrap, which exposed a pre-existing raylib draw bug: Render advanced every wrapped line by a uniform base line height (i * LineHeightInPixels), so a line following an enlarged [FontSize]/[FontScale] run was drawn on top of it instead of below. XNA already grows the per-line advance (RenderingLibrary/Graphics/Text.cs); this brings raylib to parity. Extracted GetLineTopOffsets() (public, testable) + GetLineLayoutScale() and had Render consume them only when InlineVariables are present, so the plain-text draw path stays byte-identical and allocation-free. GetLineLayoutScale reuses the same ResolveRunFont resolution DrawStyledLine lays out with (no MeasureTextEx), and the per-line factor matches what GetInlineVariableAwareWidthAndHeight already reports for size. Pin: GetLineTopOffsets_ShouldPushLineBelowEnlargedRun_NotOverlapIt (red-first via a faithful uniform extraction) asserts the line after a [FontScale=2] line sits at twice the base advance. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 95963c6 commit f201dee

3 files changed

Lines changed: 292 additions & 22 deletions

File tree

Runtimes/RaylibGum/Renderables/CustomSetPropertyOnRenderable.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -774,10 +774,13 @@ private static void SetBbCodeText(Text asText, GraphicalUiElement graphicalUiEle
774774
}
775775
}
776776

777-
// #3481: RawText was assigned above (which measured the text) before any InlineVariables
778-
// existed, so that first measurement was blind to inline [FontScale=N] runs. Re-measure now
779-
// that the runs are populated so the reported size accounts for per-line scale (otherwise a
780-
// tall run overflows its slot and overlaps the next stacked sibling).
777+
// #3481/#3532: RawText was assigned above (which wrapped + measured the text) before any
778+
// InlineVariables existed, so that first pass was blind to inline [FontScale=N]/[FontSize=N]
779+
// runs. Re-wrap first so line breaks account for an enlarged run's real size (#3532), then
780+
// re-measure so the reported size accounts for per-line scale (#3481) — otherwise a tall run
781+
// overflows its slot and overlaps the next stacked sibling, or a wide run spills past a fixed
782+
// wrap width. Mirrors the MonoGame Gum/Wireframe/CustomSetPropertyOnRenderable.cs.
783+
asText.UpdateWrappedText();
781784
asText.UpdatePreRenderDimensions();
782785
}
783786

Runtimes/RaylibGum/Renderables/Text.cs

Lines changed: 120 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,13 @@ public Text(ISystemManagers? managers)
675675
Visible = true;
676676
}
677677

678-
private void UpdateWrappedText()
678+
/// <summary>
679+
/// Re-runs the shared wrap loop (<see cref="IWrappedTextExtensions.UpdateLines"/>) to rebuild
680+
/// <see cref="WrappedText"/>. Public so the BBCode dispatch (CustomSetPropertyOnRenderable) can
681+
/// re-wrap once <see cref="InlineVariables"/> populate — the first wrap runs when RawText is set,
682+
/// before the runs exist, so it is blind to any enlarged [FontSize]/[FontScale] run (#3532).
683+
/// </summary>
684+
public void UpdateWrappedText()
679685
{
680686
mWrappedText.Clear();
681687
this.UpdateLines(mWrappedText);
@@ -775,6 +781,11 @@ public void Render(ISystemManagers managers)
775781
global::RenderingLibrary.Graphics.Renderer.Self.BatchDrawCallCounter.BeginBlendMode(Blend.Value);
776782
}
777783

784+
// Per-line vertical offsets. Plain text (no inline runs) keeps the allocation-free uniform
785+
// advance; when inline [FontSize]/[FontScale] runs can enlarge a line, each line advances by its
786+
// own height so a line after an enlarged run is not drawn on top of it (#3532).
787+
IReadOnlyList<float>? lineTopOffsets = InlineVariables.Count > 0 ? GetLineTopOffsets() : null;
788+
778789
for(int i = 0; i < WrappedText.Count; i++)
779790
{
780791
var line = WrappedText[i];
@@ -794,7 +805,9 @@ public void Render(ISystemManagers managers)
794805
}
795806

796807
var linePosition = position;
797-
linePosition.Y += i * LineHeightInPixels * LineHeightMultiplier;
808+
linePosition.Y += lineTopOffsets != null
809+
? lineTopOffsets[i]
810+
: i * LineHeightInPixels * LineHeightMultiplier;
798811

799812
var substrings = InlineVariables.Count > 0
800813
? GetStyledSubstrings(startOfLineIndex, line)
@@ -818,6 +831,53 @@ public void Render(ISystemManagers managers)
818831
}
819832
}
820833

834+
/// <summary>
835+
/// The Y offset (in pixels from this Text's top) at which each wrapped line is drawn, growing a
836+
/// line's advance by the tallest inline [FontSize]/[FontScale] run on it so a line following an
837+
/// enlarged run is placed below that run's full height rather than one base line-height down — which
838+
/// overlapped it (issue #3532). Matches the per-line height the size pass reports
839+
/// (<see cref="GetInlineVariableAwareWidthAndHeight"/>). Consumed by <see cref="Render"/>; public so
840+
/// the vertical stacking can be unit-tested without rendering.
841+
/// </summary>
842+
public IReadOnlyList<float> GetLineTopOffsets()
843+
{
844+
var offsets = new float[WrappedText.Count];
845+
float runningY = 0;
846+
int startOfLineIndex = 0;
847+
for (int i = 0; i < WrappedText.Count; i++)
848+
{
849+
offsets[i] = runningY;
850+
var line = WrappedText[i];
851+
float lineScale = GetLineLayoutScale(startOfLineIndex, line);
852+
float lineHeightFactor = FontScale > 0 ? lineScale / FontScale : 1;
853+
runningY += LineHeightInPixels * lineHeightFactor * LineHeightMultiplier;
854+
startOfLineIndex += line.Length;
855+
}
856+
return offsets;
857+
}
858+
859+
/// <summary>
860+
/// The layout scale of the wrapped line beginning at <paramref name="startOfLineIndex"/> in the
861+
/// stripped text: <see cref="FontScale"/> for a plain line, or the tallest inline
862+
/// [FontSize]/[FontScale] run's scale for a styled line (the same per-run resolution
863+
/// <see cref="DrawStyledLine"/> lays out with, so drawn and vertically-advanced sizes cannot drift).
864+
/// </summary>
865+
private float GetLineLayoutScale(int startOfLineIndex, string line)
866+
{
867+
if (InlineVariables.Count == 0)
868+
{
869+
return FontScale;
870+
}
871+
872+
var substrings = GetStyledSubstrings(startOfLineIndex, line);
873+
float maxScale = FontScale;
874+
for (int substringIndex = 0; substringIndex < substrings.Count; substringIndex++)
875+
{
876+
maxScale = System.Math.Max(maxScale, ResolveRunFont(substrings[substringIndex].Variables).LayoutScale);
877+
}
878+
return maxScale;
879+
}
880+
821881
/// <summary>
822882
/// Draws a single line with no inline styling, honoring horizontal alignment and pixel snapping.
823883
/// </summary>
@@ -1062,8 +1122,6 @@ public void UpdatePreRenderDimensions()
10621122
/// </remarks>
10631123
private void GetInlineVariableAwareWidthAndHeight(out int requiredWidth, out int requiredHeight)
10641124
{
1065-
float baseScale = FontScale;
1066-
10671125
float maxWidth = 0;
10681126
float totalHeight = 0;
10691127

@@ -1083,20 +1141,7 @@ private void GetInlineVariableAwareWidthAndHeight(out int requiredWidth, out int
10831141
}
10841142
else
10851143
{
1086-
float maxRunScale = baseScale;
1087-
float lineWidthAtScale = 0;
1088-
for (int substringIndex = 0; substringIndex < substrings.Count; substringIndex++)
1089-
{
1090-
var substring = substrings[substringIndex];
1091-
// Same resolver the draw loop uses, measured with the same per-run font at the same
1092-
// size, so a run is measured at exactly the size it is drawn - draw/measure drift is
1093-
// what reproduced the RelativeToChildren spill (#3524).
1094-
var resolvedFont = ResolveRunFont(substring.Variables);
1095-
lineWidthAtScale += MeasureTextEx(resolvedFont.Font, substring.Substring, resolvedFont.DrawSize, 0).X;
1096-
maxRunScale = System.Math.Max(maxRunScale, resolvedFont.LayoutScale);
1097-
}
1098-
lineHeightFactor = maxRunScale / baseScale;
1099-
lineWidthInBaseUnits = lineWidthAtScale / baseScale;
1144+
lineWidthInBaseUnits = MeasureStyledLineInBaseUnits(substrings, out lineHeightFactor);
11001145
}
11011146

11021147
maxWidth = System.Math.Max(maxWidth, lineWidthInBaseUnits);
@@ -1110,6 +1155,63 @@ private void GetInlineVariableAwareWidthAndHeight(out int requiredWidth, out int
11101155
requiredHeight = System.Math.Min((int)(totalHeight + .5f), MaxWidthAndHeight);
11111156
}
11121157

1158+
/// <summary>
1159+
/// Sums the base-unit width of a single line already split into styled runs, measuring each run with
1160+
/// the same per-run font/size <see cref="DrawStyledLine"/> draws it with (a [FontSize] crisp-swap or
1161+
/// scaled-atlas run, or a [FontScale] multiplier run), and reports the tallest run's height factor
1162+
/// relative to the base line height.
1163+
/// </summary>
1164+
/// <remarks>
1165+
/// Shared by the size pass (<see cref="GetInlineVariableAwareWidthAndHeight"/>, #3481/#3524) and the
1166+
/// font-aware wrap seam (<see cref="MeasureString(string, int)"/>, #3532) so the measured, drawn, and
1167+
/// wrapped size of a run cannot drift. Widths are returned in BASE units (<see cref="FontScale"/>
1168+
/// factored out), matching <see cref="MeasureString(string)"/>; <see cref="FontScale"/> is the floor,
1169+
/// so an inline run smaller than the base does not shrink the line. Guards <see cref="FontScale"/> == 0
1170+
/// because the wrap seam calls this regardless of scale (the size pass is already gated on scale &gt; 0).
1171+
/// </remarks>
1172+
private float MeasureStyledLineInBaseUnits(List<StyledSubstring> substrings, out float lineHeightFactor)
1173+
{
1174+
float baseScale = FontScale;
1175+
float maxRunScale = baseScale;
1176+
float lineWidthAtScale = 0;
1177+
for (int substringIndex = 0; substringIndex < substrings.Count; substringIndex++)
1178+
{
1179+
var substring = substrings[substringIndex];
1180+
// Same resolver the draw loop uses, measured with the same per-run font at the same
1181+
// size, so a run is measured at exactly the size it is drawn - draw/measure drift is
1182+
// what reproduced the RelativeToChildren spill (#3524).
1183+
var resolvedFont = ResolveRunFont(substring.Variables);
1184+
lineWidthAtScale += MeasureTextEx(resolvedFont.Font, substring.Substring, resolvedFont.DrawSize, 0).X;
1185+
maxRunScale = System.Math.Max(maxRunScale, resolvedFont.LayoutScale);
1186+
}
1187+
lineHeightFactor = baseScale > 0 ? maxRunScale / baseScale : 1;
1188+
return baseScale > 0 ? lineWidthAtScale / baseScale : 0;
1189+
}
1190+
1191+
/// <summary>
1192+
/// Font-aware measurement used by line wrapping (issue #3532): measures <paramref name="whatToMeasure"/>
1193+
/// (which begins at <paramref name="absoluteStartIndexInStrippedText"/> in the stripped text) honoring
1194+
/// the inline [FontSize]/[FontScale] runs active over that range, so a line containing an enlarged run
1195+
/// wraps at the run's real size instead of the base size. Falls back to the base
1196+
/// <see cref="MeasureString(string)"/> when no inline runs cover the range, keeping plain-text wrapping
1197+
/// byte-identical.
1198+
/// </summary>
1199+
public float MeasureString(string whatToMeasure, int absoluteStartIndexInStrippedText)
1200+
{
1201+
if (InlineVariables.Count == 0)
1202+
{
1203+
return MeasureString(whatToMeasure);
1204+
}
1205+
1206+
var substrings = GetStyledSubstrings(absoluteStartIndexInStrippedText, whatToMeasure);
1207+
if (substrings.Count == 0)
1208+
{
1209+
return MeasureString(whatToMeasure);
1210+
}
1211+
1212+
return MeasureStyledLineInBaseUnits(substrings, out _);
1213+
}
1214+
11131215
public void GetRequiredWidthAndHeight(IEnumerable<string> lines, out int requiredWidth, out int requiredHeight, List<float>? widths)
11141216
{
11151217

Tests/RaylibGum.Tests/Renderables/TextMarkupTests.cs

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,25 @@ namespace RaylibGum.Tests.Renderables;
1616
/// </summary>
1717
public class TextMarkupTests
1818
{
19+
// Issue #3532: the font-aware wrap seam MeasureString(string, int) must fall back to the base
20+
// MeasureString when the line carries no inline runs, so plain-text wrapping stays byte-identical.
21+
// Pins the fallback directly (independent of any font metrics) so the override can't regress plain
22+
// wrapping.
23+
[Fact]
24+
public void MeasureString_WithStartIndex_ShouldMatchBaseMeasure_WhenNoInlineVariables()
25+
{
26+
TextRuntime textRuntime = new();
27+
textRuntime.Text = "Hello World";
28+
29+
Text internalText = (Text)textRuntime.RenderableComponent;
30+
internalText.InlineVariables.ShouldBeEmpty();
31+
32+
float baseMeasure = internalText.MeasureString("Hello World");
33+
float indexedMeasure = internalText.MeasureString("Hello World", 0);
34+
35+
indexedMeasure.ShouldBe(baseMeasure);
36+
}
37+
1938
[Fact]
2039
public void Text_WithColorMarkup_PopulatesInlineVariablesAndStripsTags()
2140
{
@@ -282,4 +301,150 @@ public void WrappedTextHeight_ShouldGrow_WhenLineHasFontSizeLargerThanBase()
282301
sizedHeight.ShouldBe(plainHeight * 2, plainHeight * 0.05,
283302
"Because a [FontSize=2xBase] run is twice the base pixel size, so the measured line height doubles");
284303
}
304+
305+
// Issue #3532 (vertical stacking): a wrapped line that follows an enlarged inline run must be drawn
306+
// below that run's full height, not one base line-height down (which overlapped it). raylib advanced
307+
// every line by a uniform base line height; XNA already grows the advance per line. An explicit
308+
// newline makes the two-line split deterministic (independent of wrap width / font metrics). The
309+
// plain control derives the base per-line advance; the scaled version's second line must sit twice
310+
// as far down because its first line carries a [FontScale=2] run.
311+
[Fact]
312+
public void GetLineTopOffsets_ShouldPushLineBelowEnlargedRun_NotOverlapIt()
313+
{
314+
TextRuntime plain = new();
315+
plain.Text = "BIG\nsmall";
316+
Text plainText = (Text)plain.RenderableComponent;
317+
IReadOnlyList<float> plainOffsets = plainText.GetLineTopOffsets();
318+
plainOffsets.Count.ShouldBe(2);
319+
plainOffsets[0].ShouldBe(0);
320+
float baseLineAdvance = plainOffsets[1];
321+
baseLineAdvance.ShouldBeGreaterThan(0);
322+
323+
TextRuntime scaled = new();
324+
scaled.Text = "[FontScale=2]BIG[/FontScale]\nsmall";
325+
Text scaledText = (Text)scaled.RenderableComponent;
326+
IReadOnlyList<float> scaledOffsets = scaledText.GetLineTopOffsets();
327+
scaledOffsets.Count.ShouldBe(2);
328+
scaledOffsets[0].ShouldBe(0);
329+
scaledOffsets[1].ShouldBe(baseLineAdvance * 2, 0.01,
330+
"because the first line's [FontScale=2] run doubles its height, so the second line sits twice as far down");
331+
}
332+
333+
// Issue #3532 (the wrapping half, end-to-end): a fixed-width Text that fits "AA BB CC" at the base
334+
// size must re-wrap once the inline [FontScale=2] run enlarges "BB" past the wrap width. The BBCode
335+
// path assigns RawText (which wraps) BEFORE the InlineVariables populate, so the first wrap is blind
336+
// to the runs; the fix re-wraps font-aware afterward. raylib measures with a real font atlas, so the
337+
// wrap width can't be hardcoded — it's derived to sit strictly between the plain and enlarged
338+
// single-line widths (fits the plain line, forces the enlarged line to break). Without the fix the
339+
// enlarged text stays one line and spills past the width.
340+
[Fact]
341+
public void WrappedText_ShouldRewrapFontAware_WhenBbCodeFontScaleRunWidensLine()
342+
{
343+
// Plain single-line width (huge Absolute width -> no wrap).
344+
TextRuntime plainUnwrapped = new();
345+
plainUnwrapped.WidthUnits = DimensionUnitType.Absolute;
346+
plainUnwrapped.Width = 100000;
347+
plainUnwrapped.Text = "AA BB CC";
348+
Text plainUnwrappedText = (Text)plainUnwrapped.RenderableComponent;
349+
plainUnwrappedText.WrappedText.Count.ShouldBe(1);
350+
float plainWidth = plainUnwrappedText.WrappedTextWidth;
351+
352+
// Enlarged single-line width: the [FontScale=2] run widens the line (#3524 size reporting).
353+
TextRuntime scaledUnwrapped = new();
354+
scaledUnwrapped.WidthUnits = DimensionUnitType.Absolute;
355+
scaledUnwrapped.Width = 100000;
356+
scaledUnwrapped.Text = "AA [FontScale=2]BB[/FontScale] CC";
357+
Text scaledUnwrappedText = (Text)scaledUnwrapped.RenderableComponent;
358+
scaledUnwrappedText.WrappedText.Count.ShouldBe(1);
359+
float scaledWidth = scaledUnwrappedText.WrappedTextWidth;
360+
361+
scaledWidth.ShouldBeGreaterThan(plainWidth);
362+
363+
// A width that fits the plain line but not the enlarged line.
364+
float wrapWidth = (plainWidth + scaledWidth) / 2f;
365+
366+
// Control: plain text at this width still fits on one line, proving the width is not itself the
367+
// cause of the extra break.
368+
TextRuntime plainWrapped = new();
369+
plainWrapped.WidthUnits = DimensionUnitType.Absolute;
370+
plainWrapped.Width = wrapWidth;
371+
plainWrapped.Text = "AA BB CC";
372+
((Text)plainWrapped.RenderableComponent).WrappedText.Count.ShouldBe(1);
373+
374+
// Subject: width set BEFORE text so RawText wraps blind, then the inline run populates. The
375+
// font-aware re-wrap must break the enlarged line.
376+
TextRuntime scaled = new();
377+
scaled.WidthUnits = DimensionUnitType.Absolute;
378+
scaled.Width = wrapWidth;
379+
scaled.Text = "AA [FontScale=2]BB[/FontScale] CC";
380+
((Text)scaled.RenderableComponent).WrappedText.Count.ShouldBeGreaterThan(1,
381+
"because after the inline [FontScale] run populates, the text must re-wrap measuring the enlarged run at its own size");
382+
}
383+
384+
// Issue #3532: the FontSize-swap half of the wrap fix. With a font creator wired, [FontSize=N] swaps
385+
// in a crisp font rasterized at N px whose glyphs are wider than the base, so a fixed-width Text must
386+
// re-wrap around it. Mirrors WrappedText_ShouldRewrapFontAware_WhenBbCodeFontScaleRunWidensLine but
387+
// exercises the absolute-swap-font run path (ResolveRunFont's swap-font branch) instead of the
388+
// [FontScale] multiplier path.
389+
[Fact]
390+
public void WrappedText_ShouldRewrapFontAware_WhenBbCodeFontSizeRunWidensLine()
391+
{
392+
IRaylibFontCreator? savedCreator = CustomSetPropertyOnRenderable.InMemoryFontCreator;
393+
try
394+
{
395+
CustomSetPropertyOnRenderable.InMemoryFontCreator = new KernSmithRaylibFontCreator();
396+
397+
// Plain single-line width (huge Absolute width -> no wrap).
398+
TextRuntime plainUnwrapped = new();
399+
plainUnwrapped.Font = "Arial";
400+
plainUnwrapped.FontSize = 20;
401+
plainUnwrapped.WidthUnits = DimensionUnitType.Absolute;
402+
plainUnwrapped.Width = 100000;
403+
plainUnwrapped.Text = "AA BB CC";
404+
Text plainUnwrappedText = (Text)plainUnwrapped.RenderableComponent;
405+
plainUnwrappedText.WrappedText.Count.ShouldBe(1);
406+
float plainWidth = plainUnwrappedText.WrappedTextWidth;
407+
int targetFontSize = plainUnwrappedText.Font.BaseSize * 2;
408+
409+
// Enlarged single-line width via a size-2xBase crisp swap font (#3524 size reporting).
410+
TextRuntime swappedUnwrapped = new();
411+
swappedUnwrapped.Font = "Arial";
412+
swappedUnwrapped.FontSize = 20;
413+
swappedUnwrapped.WidthUnits = DimensionUnitType.Absolute;
414+
swappedUnwrapped.Width = 100000;
415+
swappedUnwrapped.Text = $"AA [FontSize={targetFontSize}]BB[/FontSize] CC";
416+
Text swappedUnwrappedText = (Text)swappedUnwrapped.RenderableComponent;
417+
swappedUnwrappedText.WrappedText.Count.ShouldBe(1);
418+
float swappedWidth = swappedUnwrappedText.WrappedTextWidth;
419+
420+
swappedWidth.ShouldBeGreaterThan(plainWidth);
421+
422+
// A width that fits the plain line but not the enlarged line.
423+
float wrapWidth = (plainWidth + swappedWidth) / 2f;
424+
425+
// Control: plain text at this width still fits on one line.
426+
TextRuntime plainWrapped = new();
427+
plainWrapped.Font = "Arial";
428+
plainWrapped.FontSize = 20;
429+
plainWrapped.WidthUnits = DimensionUnitType.Absolute;
430+
plainWrapped.Width = wrapWidth;
431+
plainWrapped.Text = "AA BB CC";
432+
((Text)plainWrapped.RenderableComponent).WrappedText.Count.ShouldBe(1);
433+
434+
// Subject: width set BEFORE text so RawText wraps blind, then the swap run populates. The
435+
// font-aware re-wrap must break the enlarged line.
436+
TextRuntime swapped = new();
437+
swapped.Font = "Arial";
438+
swapped.FontSize = 20;
439+
swapped.WidthUnits = DimensionUnitType.Absolute;
440+
swapped.Width = wrapWidth;
441+
swapped.Text = $"AA [FontSize={targetFontSize}]BB[/FontSize] CC";
442+
((Text)swapped.RenderableComponent).WrappedText.Count.ShouldBeGreaterThan(1,
443+
"because after the inline [FontSize] swap run populates, the text must re-wrap measuring the enlarged run at its own size");
444+
}
445+
finally
446+
{
447+
CustomSetPropertyOnRenderable.InMemoryFontCreator = savedCreator;
448+
}
449+
}
285450
}

0 commit comments

Comments
 (0)