In the attached zip file are the
FontData and
GlyphDataTable classes that I modified. I admit these changes are more than what is absolutely necessary to resolve the threading problem. So if you want the absolute minimum changes required and are willing to sacrifice a small sliver of performance, I suggest modifying the
GlyphDataTable class as follows:
Code:
public void CompleteGlyphClosure(Dictionary<int, object> glyphs)
{
int count = glyphs.Count;
int[] glyphArray = new int[glyphs.Count];
glyphs.Keys.CopyTo(glyphArray, 0);
if (!glyphs.ContainsKey(0))
{
glyphs.Add(0, null);
}
// ensure no other threads can alter the Position property of this FontData instance
lock (this.fontData)
{
for (int idx = 0; idx < count; idx++)
{
AddCompositeGlyphs(glyphs, glyphArray[idx]);
}
}
}
Regarding the attached source, the centerpiece of what I did involves abstracting all of the
Read and
Seek methods and the
Position property into an interface
IFontDataReader. I created a class named
FontData.Reader that implements the interface.
FontData has a default instance of the
FontData.Reader class that it uses internally plus a public method to obtain a new instance of the
FontData.Reader class. For the sake of code compatibility the rest of the project,
FontData also implements
IFontDataReader (dispatching the calls to its internal default instance of the
FontData.Reader class). With this approach, the modifications to
GlyphDataTable are as follows:
Code:
public void CompleteGlyphClosure(Dictionary<int, object> glyphs)
{
int count = glyphs.Count;
int[] glyphArray = new int[glyphs.Count];
glyphs.Keys.CopyTo(glyphArray, 0);
if (!glyphs.ContainsKey(0))
{
glyphs.Add(0, null);
}
IFontDataReader fontDataReader = this.fontData.NewReader();
for (int idx = 0; idx < count; idx++)
{
AddCompositeGlyphs(fontDataReader, glyphs, glyphArray[idx]);
}
}
void AddCompositeGlyphs(IFontDataReader fontDataReader, Dictionary<int, object> glyphs, int glyph)
{
//int start = this.fontData.loca.GetOffset(glyph);
int start = GetOffset(glyph);
// Has no contour?
if (start == GetOffset(glyph + 1))
return;
fontDataReader.Position = start;
int numContours = fontDataReader.ReadShort();
// Is not a composite glyph?
if (numContours >= 0)
return;
fontDataReader.SeekOffset(8);
for (; ; )
{
int flags = fontDataReader.ReadUFWord();
int cGlyph = fontDataReader.ReadUFWord();
if (!glyphs.ContainsKey(cGlyph))
glyphs.Add(cGlyph, null);
if ((flags & MORE_COMPONENTS) == 0)
return;
int offset = (flags & ARG_1_AND_2_ARE_WORDS) == 0 ? 2 : 4;
if ((flags & WE_HAVE_A_SCALE) != 0)
offset += 2;
else if ((flags & WE_HAVE_AN_X_AND_Y_SCALE) != 0)
offset += 4;
if ((flags & WE_HAVE_A_TWO_BY_TWO) != 0)
offset += 8;
fontDataReader.SeekOffset(offset);
}
}