Skip to content

Commit 1fbb7b8

Browse files
[release/7.0] GlyphSerializer rounding fix (#7721)
* GlyphSerializer rounding fix * double constants --------- Co-authored-by: Jan Kučera <[email protected]>
1 parent 5370b89 commit 1fbb7b8

File tree

1 file changed

+27
-8
lines changed

1 file changed

+27
-8
lines changed

src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/GlyphsSerializer.cs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ public GlyphsSerializer(GlyphRun glyphRun)
6464
_advances = glyphRun.AdvanceWidths;
6565
_offsets = glyphRun.GlyphOffsets;
6666

67-
_advanceWidthRoundingError = 0.0;
67+
_currentAdvanceTotal = 0.0;
68+
_idealAdvanceTotal = 0.0;
6869

6970
// "100,50,,0;".Length is a capacity estimate for an individual glyph
7071
_glyphStringBuider = new StringBuilder(10);
@@ -84,7 +85,8 @@ public GlyphsSerializer(GlyphRun glyphRun)
8485
/// </summary>
8586
public void ComputeContentStrings(out string characters, out string indices, out string caretStops)
8687
{
87-
_advanceWidthRoundingError = 0.0;
88+
_currentAdvanceTotal = 0.0;
89+
_idealAdvanceTotal = 0.0;
8890

8991
if (_clusters != null)
9092
{
@@ -188,13 +190,28 @@ private void AddGlyph(int glyph, int sourceCharacter)
188190
_glyphStringBuider.Append(GlyphSubEntrySeparator);
189191

190192
// advance width
191-
double unroundedAdvance = _advances[glyph] * _milToEm;
192-
int normalizedAdvance = (int)Math.Round(unroundedAdvance + _advanceWidthRoundingError);
193-
_advanceWidthRoundingError += (unroundedAdvance - (double)normalizedAdvance);
193+
// #7499 Advance width needs to be specified if it differs from what is in the font tables. [ECMA-388 O5.5]
194+
// Most commonly it differs after shaping, e.g. when kerning is applied. (Ex. 12-15)
195+
// XPS supports floating point values, but in the interest of file size, we want to specify integers.
196+
197+
double shapingAdvance = _advances[glyph] * _milToEm;
194198
double fontAdvance = _sideways ? _glyphTypeface.AdvanceHeights[fontIndex] : _glyphTypeface.AdvanceWidths[fontIndex];
195-
if (normalizedAdvance != (int)Math.Round(fontAdvance * EmScaleFactor))
199+
200+
// To minimize rounding errors, we keep track of the unrounded advance total as required by [M5.6].
201+
int roundedShapingAdvance = (int)Math.Round(_idealAdvanceTotal + shapingAdvance - _currentAdvanceTotal);
202+
int roundedFontAdvance = (int)Math.Round(fontAdvance);
203+
204+
if (roundedShapingAdvance != roundedFontAdvance)
196205
{
197-
_glyphStringBuider.Append(normalizedAdvance.ToString(CultureInfo.InvariantCulture));
206+
_glyphStringBuider.Append(roundedShapingAdvance.ToString(CultureInfo.InvariantCulture));
207+
_currentAdvanceTotal += roundedShapingAdvance;
208+
_idealAdvanceTotal += shapingAdvance;
209+
}
210+
else
211+
{
212+
// when the value comes from the font tables, the specification does not mandate clients to do any rounding
213+
_currentAdvanceTotal += fontAdvance;
214+
_idealAdvanceTotal += fontAdvance;
198215
}
199216

200217
_glyphStringBuider.Append(GlyphSubEntrySeparator);
@@ -326,7 +343,9 @@ private string CreateCaretStopsString()
326343

327344
private int _glyphClusterInitialOffset;
328345

329-
private double _advanceWidthRoundingError;
346+
private double _currentAdvanceTotal;
347+
348+
private double _idealAdvanceTotal;
330349

331350
private IList<ushort> _clusters;
332351

0 commit comments

Comments
 (0)