Skip to content

Commit 2db5879

Browse files
committed
Fixed bug with Nametable encoding, started work on fallback font for math symbols
1 parent 3c237b6 commit 2db5879

6 files changed

Lines changed: 271 additions & 51 deletions

File tree

src/EPPlus.Fonts.OpenType/EPPlus.Fonts.OpenType.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,13 @@
6262
</ItemGroup>
6363
<ItemGroup>
6464
<None Remove="Resources\NotoEmoji-Regular.ttf" />
65+
<None Remove="Resources\NotoSansMath-Regular.ttf" />
6566
</ItemGroup>
6667
<ItemGroup>
6768
<EmbeddedResource Include="Resources\NotoEmoji-Regular.ttf">
6869
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
6970
</EmbeddedResource>
71+
<EmbeddedResource Include="Resources\NotoSansMath-Regular.ttf" />
7072
</ItemGroup>
7173
<ItemGroup>
7274
<None Include="EPPlusLogo.png">

src/EPPlus.Fonts.OpenType/EmbeddedFonts.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ internal static OpenTypeFont LoadNotoEmoji()
2626
return LoadCached("NotoEmoji-Regular.ttf");
2727
}
2828

29+
internal static OpenTypeFont LoadNotoMath()
30+
{
31+
return LoadCached("NotoSansMath-Regular.ttf");
32+
}
33+
2934
private static OpenTypeFont LoadCached(string resourceName)
3035
{
3136
lock (_lock)
981 KB
Binary file not shown.
Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
Noto Emoji Font
2-
Copyright 2013-2024 Google Inc.
2+
Copyright 2013 Google LLC
33
Licensed under the SIL Open Font License, Version 1.1
44

55
This Font Software is licensed under the SIL Open Font License, Version 1.1.
66
This license is available with a FAQ at: https://scripts.sil.org/OFL
77

88
The font and its source code are available at:
9-
https://github.com/googlefonts/noto-emoji
9+
https://github.com/googlefonts/noto-emoji
10+
11+
12+
Noto Sans Math Font
13+
Copyright 2022 The Noto Project Authors (https://github.com/notofonts/math)
14+
Licensed under the SIL Open Font License, Version 1.1
15+
16+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
17+
This license is available with a FAQ at: https://openfontlicense.org
18+
19+
The font and its source code are available at:
20+
https://github.com/notofonts/math

src/EPPlus.Fonts.OpenType/Tables/Name/NameTable.cs

Lines changed: 90 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ private static NameRecord[] CloneRecords(NameRecord[] source)
7171
return copy;
7272
}
7373

74-
7574
internal override void SerializeInternal(FontsBinaryWriter writer, FontSerializationContext context)
7675
{
7776
// Step 1: Write header
@@ -83,9 +82,9 @@ internal override void SerializeInternal(FontsBinaryWriter writer, FontSerializa
8382
writer.WriteUInt16BigEndian(count);
8483
writer.WriteUInt16BigEndian(stringOffset);
8584

86-
87-
// Step 2: Prepare string data with deduplication
85+
// Step 2: Prepare string data with ENCODING-AWARE deduplication
8886
var stringData = new List<byte>();
87+
// FIX: Include encoding in the key to avoid collisions between different encodings
8988
var stringOffsetMap = new Dictionary<string, ushort>();
9089

9190
foreach (var record in NameRecords)
@@ -94,18 +93,21 @@ internal override void SerializeInternal(FontsBinaryWriter writer, FontSerializa
9493
var str = record.Name ?? string.Empty;
9594
var encoded = encoding.GetBytes(str);
9695

97-
if (!stringOffsetMap.TryGetValue(str, out var offset))
96+
// FIX: Create a unique key that includes both the text AND the encoding
97+
// This ensures that "Aptos Narrow" in ISO-8859-1 and UTF-16BE get different offsets
98+
string dedupKey = $"{str}|{encoding.CodePage}";
99+
100+
if (!stringOffsetMap.TryGetValue(dedupKey, out var offset))
98101
{
99102
offset = (ushort)stringData.Count;
100-
stringOffsetMap[str] = offset;
103+
stringOffsetMap[dedupKey] = offset;
101104
stringData.AddRange(encoded);
102105
}
103106

104107
record.length = (ushort)encoded.Length;
105108
record.offset = offset;
106109
}
107110

108-
109111
// Step 3: Write NameRecords
110112
foreach (var record in NameRecords)
111113
{
@@ -116,24 +118,99 @@ internal override void SerializeInternal(FontsBinaryWriter writer, FontSerializa
116118
writer.Write(stringData.ToArray());
117119
}
118120

121+
// ALSO UPDATE GetEncodingForRecord() TO RESPECT encodingId:
119122
private static Encoding GetEncodingForRecord(NameRecord record)
120123
{
121-
// Windows / Unicode – alltid säkert
122-
if (record.platformId == 3 || record.platformId == 0)
124+
// Unicode platform
125+
if (record.platformId == 0)
126+
{
123127
return Encoding.BigEndianUnicode; // UTF-16BE
128+
}
124129

125-
// Macintosh – platformId == 1
130+
// Macintosh platform
126131
if (record.platformId == 1)
127132
{
128-
// MacRoman (codepage 10000) finns inte i .NET 3.5 → fallback till ISO-8859-1
129-
// Skillnaden är minimal för västerländska typsnitt – alla vanliga tecken är identiska
130-
return Encoding.GetEncoding("ISO-8859-1");
133+
// encodingId 0 = MacRoman (single-byte)
134+
// .NET doesn't have MacRoman (codepage 10000) in all versions
135+
// Fallback to ISO-8859-1 (Latin-1)
136+
try
137+
{
138+
return Encoding.GetEncoding("ISO-8859-1");
139+
}
140+
catch
141+
{
142+
return Encoding.UTF8;
143+
}
144+
}
145+
146+
// Windows platform - CRITICAL: Must respect encodingId!
147+
if (record.platformId == 3)
148+
{
149+
return GetWindowsEncoding(record.encodingId);
131150
}
132151

133-
// Fallback – borde aldrig hända
152+
// Fallback for unknown platforms
134153
return Encoding.UTF8;
135154
}
136155

156+
// ADD GetWindowsEncoding() method:
157+
private static Encoding GetWindowsEncoding(ushort encodingId)
158+
{
159+
try
160+
{
161+
switch (encodingId)
162+
{
163+
case 0:
164+
// Symbol encoding – fallback to Windows-1252
165+
return Encoding.GetEncoding(1252);
166+
167+
case 1:
168+
// Unicode BMP (UCS-2) – UTF-16BE
169+
// THIS IS THE MOST COMMON for modern fonts!
170+
return Encoding.BigEndianUnicode;
171+
172+
case 2:
173+
// Shift-JIS – Japanese
174+
return Encoding.GetEncoding(932);
175+
176+
case 3:
177+
// GB2312 – Simplified Chinese
178+
return Encoding.GetEncoding(936);
179+
180+
case 4:
181+
// Big5 – Traditional Chinese
182+
return Encoding.GetEncoding(950);
183+
184+
case 5:
185+
// Wansung – Korean
186+
return Encoding.GetEncoding(949);
187+
188+
case 6:
189+
// Johab – Korean (alternative)
190+
return Encoding.GetEncoding(1361);
191+
192+
case 10:
193+
// Unicode full repertoire
194+
// .NET doesn't have UTF-32BE directly, use UTF-16BE
195+
return Encoding.BigEndianUnicode;
196+
197+
default:
198+
// Fallback for unknown encodings
199+
return Encoding.BigEndianUnicode;
200+
}
201+
}
202+
catch (NotSupportedException)
203+
{
204+
// If encoding doesn't exist (e.g. in .NET Standard without System.Text.Encoding.CodePages)
205+
// Fallback to UTF-16BE - this is safe for most modern fonts
206+
return Encoding.BigEndianUnicode;
207+
}
208+
catch (ArgumentException)
209+
{
210+
return Encoding.BigEndianUnicode;
211+
}
212+
}
213+
137214
//private Encoding GetEncodingForRecord(NameRecord record)
138215
//{
139216
// if (record.platformId == 0)

0 commit comments

Comments
 (0)