@@ -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