Edit: So, I see what's happening, I just don't understand why. My Dictionary<string, object>
is storing the fonts just fine, but there's a particular function that runs, and when it runs, the Dictionary gets partly wiped out.
The code that is running between these images is:
public static int countLines(string a)
{
int count = 0;
string line;
System.IO.StreamReader file;
try
{
file = new System.IO.StreamReader(@a);
}
catch (Exception ex)
{
MessageBox.Show("Missing " + a + "\n\n" + ex.ToString(), "Exception");
return 0;
}
while ((line = file.ReadLine()) != null)
{
if (line.Substring(0, 2) == "10") { count++; }
}
file.Close();
return count;
}
---END EDIT---
I'm getting this tracedump when I use too many custom fonts. I'm using the same solution listed here, and have tried the voted fix (removing the dispose and the one marshalling call) which just leads immediately to a GDI+ error. Actually, I have a modified version of that code, so that I can call and get different fonts:
#region Font setup
//Setup code for custom fonts
[DllImport("gdi32.dll")]
private static extern IntPtr AddFontMemResourceEx(byte[] pbFont, int cbFont, IntPtr pdv, out uint pcFonts);
[DllImport("gdi32.dll")]
internal static extern bool RemoveFontMemResourceEx(IntPtr fh);
static private IntPtr m_fh = IntPtr.Zero;
static private PrivateFontCollection m_pfc = null;
public static Font getFont(string fonttype, float size)
{
m_pfc = null;
Font fnt = null;
if (null == m_pfc)
{
Stream stmFont = null;
// First load the font as a memory stream
switch (fonttype)
{
case "MICR":
stmFont = Assembly.GetExecutingAssembly().GetManifestResourceStream("QATestFileGenTools.Fonts.MICR.ttf");
break;
case "HAND":
Random rnd = new Random(DateTime.Now.Millisecond);
int rr = rnd.Next(0, 3);
if (rr == 0)
{
stmFont = Assembly.GetExecutingAssembly().GetManifestResourceStream("QATestFileGenTools.Fonts.hand1.ttf");
size += rnd.Next(0, 4);
}
if (rr == 1)
{
stmFont = Assembly.GetExecutingAssembly().GetManifestResourceStream("QATestFileGenTools.Fonts.hand2.ttf");
size += rnd.Next(2, 6);
}
if (rr == 2)
{
stmFont = Assembly.GetExecutingAssembly().GetManifestResourceStream("QATestFileGenTools.Fonts.hand3.ttf");
size += rnd.Next(6, 10);
}
break;
case "SIG":
stmFont = Assembly.GetExecutingAssembly().GetManifestResourceStream("QATestFileGenTools.Fonts.signature.ttf");
break;
default:
MessageBox.Show("Bad call to getFont()", "Error");
break;
}
if (null != stmFont)
{
//
// GDI+ wants a pointer to memory, GDI wants the memory.
// We will make them both happy.
//
// First read the font into a buffer
byte[] rgbyt = new Byte[stmFont.Length];
stmFont.Read(rgbyt, 0, rgbyt.Length);
// Then do the unmanaged font (Windows 2000 and later)
// The reason this works is that GDI+ will create a font object for
// controls like the RichTextBox and this call will make sure that GDI
// recognizes the font name, later.
uint cFonts;
AddFontMemResourceEx(rgbyt, rgbyt.Length, IntPtr.Zero, out cFonts);
// Now do the managed font
IntPtr pbyt = Marshal.AllocCoTaskMem(rgbyt.Length);
if (null != pbyt)
{
Marshal.Copy(rgbyt, 0, pbyt, rgbyt.Length);
m_pfc = new PrivateFontCollection();
m_pfc.AddMemoryFont(pbyt, rgbyt.Length);
Marshal.FreeCoTaskMem(pbyt);
}
}
}
if (m_pfc.Families.Length > 0)
{
// Handy how one of the Font constructors takes a
// FontFamily object, huh? :-)
fnt = new Font(m_pfc.Families[0], size);
}
m_pfc.Dispose();
return fnt;
}
//End setup for fonts
#endregion
The GDI+ error I inevitably get if I remove the dispose() and the FreeCoTaskMem() is this.
Neither issue occurs immediately. It appears that I can run the call on average 20-25 times before either exception is thrown.
Any thoughts would be appreciated. When I was JUST using a single font, I never had an error (could generate 1000+ every time). It appears, anecdotally at least, that if I'm only ever calling two fonts, it's also not a problem. But when "HAND" is tossed into the mix, that seems to be the issue.
The method that calls it is:
public static byte[] GenCheckImage(string outputPath, string frontBack, string MICRline, string amount, string[] imageOptions, Dictionary<string, object> dictCfgIn, string names = "John Smith", string date = null, string lar = null)//, string payeeName, string date)
{
System.Reflection.Assembly thisExe;
thisExe = System.Reflection.Assembly.GetExecutingAssembly();
if (date == null)
{
date = DateTime.Now.AddDays(-4).ToString("d");
}
if (lar == null)
{
lar = "Some Dollars & xx/100";
}
if (frontBack == "F")
{
//Open the front image to frontImage Image source from embedded resource
System.IO.Stream file;
Random rnd = new Random(DateTime.Now.Millisecond);
int a = rnd.Next(imageOptions.Length);
// if (a == 0) {file = thisExe.GetManifestResourceStream("QATestFileGenTools.checkFront1.bmp");}
// else if (a == 1) {file = thisExe.GetManifestResourceStream("QATestFileGenTools.checkFront2.bmp");}
file = thisExe.GetManifestResourceStream(imageOptions[a]);
Bitmap b;
using (System.Drawing.Image frontImage = System.Drawing.Image.FromStream(file))//;
{
byte[] binaryData = new Byte[file.Length];
long bytesRead = file.Read(binaryData, 0,
(int)file.Length);
Bitmap b2 = new Bitmap(frontImage);
file.Close();
Font micrFont = getFont("MICR", 18);
Font objFont;
if ((bool)dictCfgIn["Handwriting"])
objFont = getFont("HAND", 20);
else
objFont = new System.Drawing.Font("Arial", 20);
Font sigFont = getFont("SIG", 30);
string[] sigName = NSCreateRAW.CreateRaw.genName();
string sigString = sigName[0] + sigName[1] + rnd.Next(1, 6).ToString();
amount = amount.TrimStart('0'); //Insert(2, "XYZ")
amount = amount.Insert((amount.Length - 2), ".");
//Draw something
//Bitmap b = new Bitmap(frontImage);
b = new Bitmap(frontImage);
Graphics graphics = Graphics.FromImage(b);
if (imageOptions[a] == "QATestFileGenTools.checkFront1.bmp") // ProfitStars Check Template
{
graphics.DrawString(date, objFont, Brushes.Black, 690, 150);
graphics.DrawString(lar, objFont, Brushes.Black, 150, 280);
graphics.DrawString(names, objFont, Brushes.Black, 200, 215);
graphics.DrawString(MICRline, micrFont, Brushes.Black, 200, 490);
graphics.DrawString(amount, objFont, Brushes.Black, 985, 230);
graphics.DrawString(sigString, sigFont, Brushes.Black, 680, 400);
}
else if (imageOptions[a] == "QATestFileGenTools.checkFront2.bmp") // TWS Check Template
{
graphics.DrawString(date, objFont, Brushes.Black, 735, 105);
graphics.DrawString(lar, objFont, Brushes.Black, 160, 250);
graphics.DrawString(names, objFont, Brushes.Black, 200, 165);
graphics.DrawString(MICRline, micrFont, Brushes.Black, 200, 490);
graphics.DrawString(amount, objFont, Brushes.Black, 1020, 185);
graphics.DrawString(sigString, sigFont, Brushes.Black, 690, 380);
}
else if (imageOptions[a] == "QATestFileGenTools.checkFront3.bmp") // "Blank" Check Template
{
graphics.DrawString(date, objFont, Brushes.Black, 935, 110);
graphics.DrawString(lar, objFont, Brushes.Black, 180, 275);
graphics.DrawString(names, objFont, Brushes.Black, 220, 210);
graphics.DrawString(MICRline, micrFont, Brushes.Black, 220, 470);
graphics.DrawString(amount, objFont, Brushes.Black, 1000, 210);
graphics.DrawString(sigString, sigFont, Brushes.Black, 690, 395);
}
else
{
//graphics.DrawString(MICRline, micrFont, Brushes.Black, 200, 490);
//graphics.DrawString(amount, objFont, Brushes.Black, 985, 230);
}
if ((bool)dictCfgIn["Bad Images"] && (bool)dictCfgIn["Skew Images"])
{
Point[] destinationPoints = {
new Point(0, 50), // destination for upper-left point of
// original
new Point(1170, 0), // destination for upper-right point of
// original
new Point(30, 585)};
graphics.DrawImage(b, destinationPoints);
}
if ((bool)dictCfgIn["Bad Images"] && (bool)dictCfgIn["Bar Images"])
{
Pen penWide = new Pen(Color.Black, 45);
Pen penNarrow = new Pen(Color.Black, 2);
Point[] points1 =
{
new Point(0 , 250),
new Point(1200, 250)
};
Point[] points2 =
{
new Point (0 , 280),
new Point (1200, 280)
};
graphics.DrawLines(penWide, points1);
graphics.DrawLines(penNarrow, points2);
}
}
//Convert to TIF - requires BitMiracle.LibTiff.Classic
byte[] tiffBytes = GetTiffImageBytes(b, false);
//File.WriteAllBytes("checkfront.tif", tiffBytes);
//Encoding enc = Encoding.GetEncoding(1252);
//string result = enc.GetString(tiffBytes);
//File.Write("check2.tif", result);
return tiffBytes;
}
The using
wrapper in the second bit of code is new - I thought it might help, but it didn't change the outcome at all.