Skip to content

Commit bbdada2

Browse files
committed
First working version with TextShaping and subsetting
1 parent 0aa9fea commit bbdada2

11 files changed

Lines changed: 36 additions & 48 deletions

src/EPPlus.Fonts.OpenType/FontSubsetBuilder.cs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,10 @@ Date Author Change
1010
*************************************************************************************************
1111
10/07/2025 EPPlus Software AB EPPlus.Fonts.OpenType 1.0
1212
*************************************************************************************************/
13-
using EPPlus.Fonts.OpenType.Tables;
1413
using EPPlus.Fonts.OpenType.Tables.Glyph;
1514
using EPPlus.Fonts.OpenType.Tables.Head;
1615
using EPPlus.Fonts.OpenType.Tables.Loca;
17-
using System;
1816
using System.Collections.Generic;
19-
using System.Linq;
20-
using System.Text;
2117

2218
namespace EPPlus.Fonts.OpenType
2319
{
@@ -49,18 +45,14 @@ public OpenTypeFont BuildSubset(HashSet<ushort> glyphIds, IEnumerable<char> used
4945
? HeadTable.IndexToLocFormats.Offset16
5046
: HeadTable.IndexToLocFormats.Offset32;
5147

52-
// Uppdatera HeadTable också
48+
// Update HeadTable
5349
subsetFont.HeadTable.IndexToLocFormat = indexToLocFormat;
5450

55-
// Bygg Loca-tabellen
51+
// Build Loca-table for the subset
5652
subsetFont.AddOrReplaceTable(
5753
LocaTable.CreateSubset(glyphSubsetResult.LocaOffsets, indexToLocFormat)
5854
);
5955

60-
//subsetFont.ReplaceTable(TableNames.Hmtx, BuildHmtxSubset(glyphIds));
61-
//subsetFont.ReplaceTable(TableNames.Cmap, BuildCmapSubset(usedChars));
62-
63-
//subsetFont.RecalculateChecksums();
6456
return subsetFont;
6557
}
6658
}

src/EPPlus.Fonts.OpenType/FontTableReaderFactory.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1-
using System;
1+
/*************************************************************************************************
2+
Required Notice: Copyright (C) EPPlus Software AB.
3+
This software is licensed under PolyForm Noncommercial License 1.0.0
4+
and may only be used for noncommercial purposes
5+
https://polyformproject.org/licenses/noncommercial/1.0.0/
6+
7+
A commercial license to use this software can be purchased at https://epplussoftware.com
8+
*************************************************************************************************
9+
Date Author Change
10+
*************************************************************************************************
11+
10/07/2025 EPPlus Software AB EPPlus.Fonts.OpenType 1.0
12+
*************************************************************************************************/
13+
using System;
214
using System.IO;
315

416
namespace EPPlus.Fonts.OpenType

src/EPPlus.Fonts.OpenType/OpenTypeFontFactory.cs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,21 @@ internal static class OpenTypeFontFactory
1919
{
2020
public static OpenTypeFont CreateFromFace(FontFaceInfo face)
2121
{
22-
// Read entire file into memory first
2322
byte[] fontData = File.ReadAllBytes(face.FilePath);
24-
var stream = new MemoryStream(fontData);
2523

26-
//var reader = new FontsBinaryReader(stream);
27-
//reader.BaseStream.Position = face.OffsetInFile;
28-
29-
var format = face.OffsetInFile > 0
30-
? FontFormat.Ttf
31-
: Path.GetExtension(face.FilePath).ToLowerInvariant() == ".otf"
32-
? FontFormat.Otf
33-
: FontFormat.Ttf;
34-
35-
return new OpenTypeFont(fontData, format);
24+
if (face.OffsetInFile > 0) // Font inside TTC
25+
{
26+
// Pass the start offset to the constructor
27+
// This tells OpenTypeFont where this font's table directory starts
28+
return new OpenTypeFont(fontData, face.OffsetInFile, FontFormat.Ttf);
29+
}
30+
else // Regular TTF/OTF
31+
{
32+
var format = Path.GetExtension(face.FilePath).ToLowerInvariant() == ".otf"
33+
? FontFormat.Otf
34+
: FontFormat.Ttf;
35+
return new OpenTypeFont(fontData, format);
36+
}
3637
}
3738

3839
public static OpenTypeFont CreateFromBytes(byte[] bytes, FontFormat format)

src/EPPlus.Fonts.OpenType/OpenTypeFontSerializer.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ public OpenTypeFontSerializer(OpenTypeFont font)
2727
_font = font ?? throw new ArgumentNullException(nameof(font));
2828
}
2929

30-
// In OpenTypeFontSerializer class
31-
3230
public byte[] Serialize()
3331
{
3432
using (var stream = new MemoryStream())

src/EPPlus.Fonts.OpenType/Scanner/FontScannerV2.Ttc.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ ex is InvalidOperationException ||
9898
}
9999
}
100100

101+
102+
101103
return faces;
102104
}
103105
}

src/EPPlus.Fonts.OpenType/Scanner/FontScannerV2Core.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ internal static FontFaceInfo ScanSingleFace(string filePath, long offset)
7070

7171
for (int i = 0; i < numTables; i++)
7272
{
73+
long tagPos = fs.Position;
7374
var record = new TableRecord
7475
{
7576
Tag = new Tag(reader),

src/EPPlus.Fonts.OpenType/Subsetting/CmapSubsetProcessor.cs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@ public void Rewrite(FontSubsettingContext context)
5555
// Build mapping: Unicode code point → NEW glyph ID in subset
5656
Dictionary<uint, ushort> cmapMapping = new Dictionary<uint, ushort>();
5757

58-
Console.WriteLine("\n=== CMAP REWRITE DEBUG ===");
59-
6058
foreach (uint codePoint in context.UsedCodePoints)
6159
{
6260
ushort oldGid;
@@ -67,11 +65,6 @@ public void Rewrite(FontSubsettingContext context)
6765
if (context.OldToNewGlyphId.TryGetValue(oldGid, out newGid))
6866
{
6967
cmapMapping[codePoint] = newGid;
70-
// Debug first few
71-
if (codePoint >= 'a' && codePoint <= 'c')
72-
{
73-
Console.WriteLine($" U+{codePoint:X4} ('{(char)codePoint}') : oldGID {oldGid:X4} -> newGID {newGid:X4}");
74-
}
7568
}
7669
else
7770
{

src/EPPlus.Fonts.OpenType/Subsetting/GlyphAndLocaSubsetProcessor.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,6 @@ public void Rewrite(FontSubsettingContext context)
7979

8080
offsets.Add((uint)ms.Position);
8181
}
82-
83-
Console.WriteLine($"Total glyf table size: {ms.Position} bytes, loca last offset: {offsets[offsets.Count - 1]}");
8482
}
8583

8684
// 4. Update head and create loca

src/EPPlus.Fonts.OpenType/Subsetting/SubsetFontBuilder.cs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -80,28 +80,14 @@ private void BuildGlyphMapping(FontSubsettingContext context)
8080
var sortedGlyphs = new List<ushort>(context.IncludedGlyphs);
8181
sortedGlyphs.Sort();
8282

83-
Console.WriteLine($"\n=== BUILD GLYPH MAPPING DEBUG ===");
84-
Console.WriteLine($"Total included glyphs: {sortedGlyphs.Count}");
85-
Console.WriteLine($"First 10: {string.Join(", ", sortedGlyphs.Take(10).Select(g => $"{g:X4}").ToArray())}");
86-
Console.WriteLine($"Around 'a' (looking for 0x0045):");
8783

8884
for (ushort newId = 0; newId < sortedGlyphs.Count; newId++)
8985
{
9086
ushort oldId = sortedGlyphs[newId];
9187
context.OldToNewGlyphId[oldId] = newId;
9288
context.NewToOldGlyphId.Add(oldId);
9389

94-
if (oldId >= 0x0043 && oldId <= 0x0048)
95-
{
96-
Console.WriteLine($" oldGID {oldId:X4} -> newGID {newId:X4}");
97-
}
9890
}
99-
Console.WriteLine($"All {sortedGlyphs.Count} glyphs after sort:");
100-
for (int i = 0; i < Math.Min(50, sortedGlyphs.Count); i++)
101-
{
102-
Console.WriteLine($" [{i}] = oldGID {sortedGlyphs[i]:X4}");
103-
}
104-
Console.WriteLine($"==================================\n");
10591
}
10692
}
10793
}

src/EPPlus.Fonts.OpenType/Tables/Glyph/Glyph.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ public int GetSize()
4040

4141
internal override void Serialize(FontsBinaryWriter writer)
4242
{
43+
if (Header.numberOfContours == 0 && SimpleData == null && CompositeData == null)
44+
{
45+
return; // Don't write anything for empty glyphs!
46+
}
4347
Header.Serialize(writer);
4448

4549
if (Header.numberOfContours > 0 && SimpleData != null)

0 commit comments

Comments
 (0)