Skip to content

Commit cab5ca4

Browse files
swmalswmal
authored andcommitted
Added AdvanceWidth to VerticalShapedGlyph
1 parent 30afb0c commit cab5ca4

4 files changed

Lines changed: 82 additions & 2 deletions

File tree

src/EPPlus.Fonts.OpenType.Tests/Subsetting/VerticalSubsettingTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*************************************************************************************************
1+
 /*************************************************************************************************
22
Required Notice: Copyright (C) EPPlus Software AB.
33
This software is licensed under PolyForm Noncommercial License 1.0.0
44
and may only be used for noncommercial purposes

src/EPPlus.Fonts.OpenType.Tests/TextShaping/VerticalTextShapingTests.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,5 +296,73 @@ public void ShapeLightVertical_SurrogatePair_ProducesOneEntryWithCharCountTwo()
296296
}
297297

298298
#endregion
299+
300+
#region Centering tests
301+
302+
[TestMethod]
303+
public void ShapeVertical_CjkText_GlyphsHavePositiveAdvanceWidth()
304+
{
305+
// Arrange
306+
var font = OpenTypeFonts.GetFontDataOpen(FontFolders, "BIZ UDGothic", FontSubFamily.Regular, true);
307+
var shaper = new TextShaper(font);
308+
309+
// Act
310+
var result = shaper.ShapeVertical("日本語");
311+
312+
// Assert - AdvanceWidth is required for horizontal centering in vertical text columns
313+
foreach (var glyph in result.Glyphs)
314+
{
315+
Assert.IsTrue(glyph.AdvanceWidth > 0,
316+
$"Glyph {glyph.GlyphId} has AdvanceWidth {glyph.AdvanceWidth}, expected > 0");
317+
}
318+
}
319+
320+
[TestMethod]
321+
public void ShapeVertical_AdvanceWidthMatchesHmtxAdvanceWidth()
322+
{
323+
// Arrange - AdvanceWidth on VerticalShapedGlyph must equal hmtx advanceWidth
324+
// since centering calculations depend on this value being accurate
325+
var font = OpenTypeFonts.GetFontDataOpen(FontFolders, "BIZ UDGothic", FontSubFamily.Regular, true);
326+
var shaper = new TextShaper(font);
327+
328+
// Act
329+
var result = shaper.ShapeVertical("日本");
330+
331+
// Assert
332+
foreach (var glyph in result.Glyphs)
333+
{
334+
var expectedAdvanceWidth = font.HmtxTable.GetAdvanceWidth(glyph.GlyphId);
335+
Assert.AreEqual(expectedAdvanceWidth, glyph.AdvanceWidth,
336+
$"Glyph {glyph.GlyphId}: AdvanceWidth {glyph.AdvanceWidth} " +
337+
$"does not match hmtx advanceWidth {expectedAdvanceWidth}");
338+
}
339+
}
340+
341+
[TestMethod]
342+
public void ShapeVertical_FontWithoutVmtx_AdvanceWidthMatchesHmtxAdvanceWidth()
343+
{
344+
// Arrange - Roboto has no vmtx table, both YAdvance and AdvanceWidth
345+
// should fall back to hmtx and be equal to each other
346+
var font = OpenTypeFonts.GetFontDataOpen(FontFolders, "Roboto", FontSubFamily.Regular, true);
347+
var shaper = new TextShaper(font);
348+
Assert.IsNull(font.VmtxTable, "Roboto should not have a vmtx table");
349+
350+
// Act
351+
var result = shaper.ShapeVertical("ABC");
352+
353+
// Assert
354+
foreach (var glyph in result.Glyphs)
355+
{
356+
var expectedAdvanceWidth = font.HmtxTable.GetAdvanceWidth(glyph.GlyphId);
357+
Assert.AreEqual(expectedAdvanceWidth, glyph.AdvanceWidth,
358+
$"Glyph {glyph.GlyphId}: AdvanceWidth {glyph.AdvanceWidth} " +
359+
$"does not match hmtx advanceWidth {expectedAdvanceWidth}");
360+
Assert.AreEqual(glyph.YAdvance, glyph.AdvanceWidth,
361+
$"Glyph {glyph.GlyphId}: YAdvance and AdvanceWidth should be equal " +
362+
$"when falling back to hmtx (YAdvance={glyph.YAdvance}, AdvanceWidth={glyph.AdvanceWidth})");
363+
}
364+
}
365+
366+
#endregion
299367
}
300368
}

src/EPPlus.Fonts.OpenType/TextShaping/TextShaper.Vertical.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,14 @@ private List<VerticalShapedGlyph> MapToVerticalGlyphs(string text)
110110
ushort advanceHeight;
111111
short topSideBearing;
112112
GetVerticalMetrics(font, glyphId, out advanceHeight, out topSideBearing);
113+
// Fetch horizontal advance width for centering
114+
ushort advanceWidth = font.HmtxTable.GetAdvanceWidth(glyphId);
113115

114116
glyphs.Add(new VerticalShapedGlyph(
115117
glyphId,
116118
advanceHeight,
117119
topSideBearing,
120+
advanceWidth,
118121
(ushort)i,
119122
(byte)charCount,
120123
fontId

src/EPPlus.Interfaces/Drawing/Text/VerticalShapedGlyph.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ public class VerticalShapedGlyph
4343
/// </summary>
4444
public short TopSideBearing;
4545

46+
/// <summary>
47+
/// Horizontal advance width in font design units.
48+
/// Sourced from the 'hmtx' table.
49+
/// Used by the renderer to center the glyph horizontally in a vertical text column:
50+
/// xOffset = (columnWidth - AdvanceWidth) / 2
51+
/// </summary>
52+
public ushort AdvanceWidth;
53+
4654
/// <summary>
4755
/// Index of the original character that produced this glyph.
4856
/// Used to map shaped glyphs back to character positions.
@@ -75,12 +83,13 @@ public VerticalShapedGlyph()
7583
/// <summary>
7684
/// Creates a new vertical shaped glyph with all fields specified.
7785
/// </summary>
78-
public VerticalShapedGlyph(ushort glyphId, ushort yAdvance, short topSideBearing,
86+
public VerticalShapedGlyph(ushort glyphId, ushort yAdvance, short topSideBearing, ushort advanceWidth,
7987
ushort clusterIndex, byte charCount, byte fontId = 0)
8088
{
8189
GlyphId = glyphId;
8290
YAdvance = yAdvance;
8391
TopSideBearing = topSideBearing;
92+
AdvanceWidth = advanceWidth;
8493
ClusterIndex = clusterIndex;
8594
CharCount = charCount;
8695
FontId = fontId;

0 commit comments

Comments
 (0)