1: <%@ WebHandler Language="C#" Class="heading" %>
2: /*
3: Dynamic Heading Generator
4: By Stewart Rosenberger
5: http://www.stewartspeak.com/headings/
6: *
7: * C#.NET conversion by Robert S. Robbins
8:
9: This script generates PNG images of text, written in
10: the font/size that you specify. These PNG images are passed
11: back to the browser. Optionally, they can be cached for later use.
12: If a cached image is found, a new image will not be generated,
13: and the existing copy will be sent to the browser.
14:
15: Additional documentation on PHP's image handling capabilities can
16: be found at http://www.php.net/image/
17: *
18: * Additional documentation on the GD-Sharp .NET wrapper for the GD Library can
19: * be found at http://gd-sharp.sourceforge.net/
20: */
21: using System;
22: using System.Web;
23: using System.Diagnostics;
24: using System.Runtime.InteropServices;
25: using System.IO;
26: using System.Drawing;
27: using Ntx.GD;
28: using System.Collections;
29: using System.Security.Cryptography;
30: using System.Text;
31:
32: public class heading : IHttpHandler { 33:
34: private string font_file = @"C:\WINDOWS\Fonts\comic.ttf";
35: private string font_name = "Comic Sans MS";
36: private int font_size = 30;
37: private string font_color = "#000000";
38: private string background_color = "#ffffff";
39: private bool transparent_background = true;
40: private bool cache_images = true;
41: private string cache_folder = @"D:\inetpub\williamsportwebdeveloper\cache";
42:
43: /*
44: ---------------------------------------------------------------------------
45: For basic usage, you should not need to edit anything below this comment.
46: If you need to further customize this script's abilities, make sure you
47: are familiar with PHP and C#.NET and its image handling capabilities.
48: ---------------------------------------------------------------------------
49: */
50:
51: private string mime_type = "image/png";
52: private string extension = ".png";
53: private int send_buffer_size = 4096;
54:
55: public void ProcessRequest (HttpContext context) { 56: // create trace listener file for debugging purposes
57: System.IO.Stream objFile = System.IO.File.Create(@"D:\inetpub\williamsportwebdeveloper\app_data\trace.txt");
58: TextWriterTraceListener objTextListener = new TextWriterTraceListener(objFile);
59: Trace.Listeners.Add(objTextListener);
60: Trace.AutoFlush = true;
61:
62: try
63: { 64: // This is the equivalent of calling ImageCreate
65: GD img = new GD(1, 1, true);
66:
67: if (HttpContext.Current.Request.QueryString["text"] == null)
68: { 69: Trace.WriteLine("Fatal Error: No text specified."); 70: }
71:
72: // clean up text
73: string text = HttpContext.Current.Request.QueryString["text"];
74: text = text.Replace(@"\", "");
75:
76: // look for cached copy, send if it exists
77: string hash = GenerateMD5Hash(font_name, font_size.ToString(), font_color, background_color, transparent_background.ToString(), text);
78: string cache_filename = cache_folder + @"\" + hash + extension;
79: if (cache_images)
80: { 81: // check image file availability
82: if (File.Exists(cache_filename))
83: { 84: Trace.WriteLine("Serving image file: " + cache_filename + " from the cache"); 85: context.Response.ContentType = mime_type;
86:
87: FileStream fs = File.OpenRead(cache_filename);
88: byte[] buffer = ReadFully(fs);
89: context.Response.BinaryWrite(buffer);
90: context.Response.Flush();
91:
92: // exit immediately
93: Trace.Close();
94: Trace.Flush();
95: return;
96: }
97: }
98:
99: // check font availability
100: if (File.Exists(font_file) == false)
101: { 102: Trace.WriteLine("Fatal Error: The server is missing the specified font."); 103: }
104:
105: // create image
106: int dip = get_dip(font_name, font_size);
107: int[] box = ImageTTFBBox(font_name, font_size, text);
108: // This is the equivalent of calling ImageCreate
109: GD image = new GD(box[0], box[1], true);
110: if (image == null)
111: { 112: Trace.WriteLine("Fatal Error: The server could not create this heading image."); 113: }
114:
115: // allocate colors and draw text
116: Color font_rgb = Color.FromArgb(ColorTranslator.FromHtml(font_color).ToArgb());
117: GDColor fg = image.ColorAllocate(font_rgb.R, font_rgb.G, font_rgb.B);
118: Color background_rgb = Color.FromArgb(ColorTranslator.FromHtml(background_color).ToArgb());
119: GDColor bg = image.ColorAllocate(background_rgb.R, background_rgb.G, background_rgb.B);
120: image.FilledRectangle(0, 0, box[0], box[0], bg);
121: // bounding rectangle
122: ArrayList br = new ArrayList();
123: br.Add(new Ntx.GD.Point(0, 0));
124: br.Add(new Ntx.GD.Point(box[0], 0));
125: br.Add(new Ntx.GD.Point(box[0], box[1]));
126: br.Add(new Ntx.GD.Point(0, box[1]));
127: // This is the equivalent of calling ImageTTFText
128: string result = image.StringFT(br, fg, font_file, font_size, 0, 0, font_size, text, true);
129: Trace.WriteLine(result, "result");
130:
131: // set transparency
132: if (transparent_background)
133: { 134: image.ColorTransparent(bg);
135: }
136:
137: // save copy of image for cache
138: if(cache_images)
139: { 140: image.Save(GD.FileType.Png, cache_filename, 1);
141: }
142:
143: context.Response.ContentType = mime_type;
144: MemoryStream ms = new MemoryStream();
145: image.Save(GD.FileType.Png, ms);
146: byte[] binary = ms.ToArray();
147: context.Response.BinaryWrite(binary);
148: context.Response.Flush();
149: }
150: catch (Exception ex)
151: { 152: Trace.WriteLine("Fatal Error: Server does not support PHP image generation"); 153: Trace.WriteLine(ex.ToString());
154: }
155: finally
156: { 157: Trace.Close();
158: Trace.Flush();
159: }
160: }
161:
162: /// <summary>
163: /// Try to determine the "dip" (pixels dropped below baseline) of this
164: /// font for this size.
165: /// </summary>
166: /// <param name="font">The name of a TTF font.</param>
167: /// <param name="size">The size of the font.</param>
168: /// <returns>CellDescent, the height below base line.</returns>
169: /// <remarks>Not exactly the same as the original function, but the best we can do.</remarks>
170: public int get_dip(string font, int size)
171: { 172: // Create the Font object for the font at that size
173: System.Drawing.Font objFont = new System.Drawing.Font(font, size, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Pixel);
174:
175: // Determine the CellDescent
176: int intCellDescent = size * objFont.FontFamily.GetCellDescent(objFont.Style) / objFont.FontFamily.GetEmHeight(objFont.Style);
177: Trace.WriteLine(size.ToString(), "size");
178: Trace.WriteLine(objFont.FontFamily.GetCellDescent(objFont.Style).ToString(), "CellDescent");
179: Trace.WriteLine(objFont.FontFamily.GetEmHeight(objFont.Style).ToString(), "GetEmHeight");
180: Trace.WriteLine(intCellDescent.ToString(), "intCellDescent");
181: return intCellDescent;
182: }
183:
184: /// <summary>
185: /// Give the bounding box of a text using TrueType fonts
186: /// </summary>
187: /// <param name="font">The name of a TTF font.</param>
188: /// <param name="size">The size of the font.</param>
189: /// <param name="text">The text to be displayed using the font.</param>
190: /// <returns>An integer array containing the height and width.</returns>
191: /// <remarks>Not exactly the same as the original function, but the best we can do.</remarks>
192: public int[] ImageTTFBBox(string font, int size, string text)
193: { 194: Bitmap objBmpImage = new Bitmap(1, 1);
195:
196: int intWidth = 0;
197: int intHeight = 0;
198:
199: System.Drawing.Font objFont = new System.Drawing.Font(font, size, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Pixel);
200:
201: // Create a graphics object to measure the text’s width and height.
202: Graphics objGraphics = Graphics.FromImage(objBmpImage);
203:
204: // Determine the bitmap size.
205: /* Need to multiply the width by 1.25.
206: * This is an ugly hack.
207: * The System.Drawing width will not match the GD Library drawing width.
208: */
209: intWidth = Convert.ToInt32(objGraphics.MeasureString(text, objFont).Width * 1.25);
210: intHeight = Convert.ToInt32(objGraphics.MeasureString(text, objFont).Height);
211: Trace.WriteLine(intWidth.ToString(), "intWidth");
212: Trace.WriteLine(intHeight.ToString(), "intHeight");
213: int[] box = { intWidth, intHeight }; 214: return box;
215: }
216:
217: /// <summary>
218: /// Generates a MD5 hash for an unique file name
219: /// </summary>
220: /// <param name="font_name">The name of a TTF font.</param>
221: /// <param name="font_size">The size of the font.</param>
222: /// <param name="font_color">The color of the font.</param>
223: /// <param name="background_color">The background color of the text image.</param>
224: /// <param name="transparent_color">The transparent color of the text image.</param>
225: /// <param name="text">The text of the text image.</param>
226: /// <returns>A string to be used as an unique file name.</returns>
227: private string GenerateMD5Hash(string font_name, string font_size, string font_color, string background_color, string transparent_background, string text)
228: { 229: // Create an instance of the MD5CryptoServiceProvider class
230: MD5CryptoServiceProvider md5Hasher = new MD5CryptoServiceProvider();
231: // The array of bytes that will contain the encrypted value
232: byte[] hashedBytes;
233: UTF8Encoding encoder = new UTF8Encoding();
234: // Call ComputeHash, passing in the plain-text string as an array of bytes
235: // The return value is the encrypted value, converted to a string
236: hashedBytes = md5Hasher.ComputeHash(encoder.GetBytes((font_name + font_size + font_color + background_color + transparent_background + text)));
237: Trace.WriteLine(BitConverter.ToString(hashedBytes).Replace("-", "").ToLower(), "BitConverter"); 238: return BitConverter.ToString(hashedBytes).Replace("-", "").ToLower(); 239: }
240:
241: /// <summary>
242: /// Reads data from a stream until the end is reached. The
243: /// data is returned as a byte array. An IOException is
244: /// thrown if any of the underlying IO calls fail.
245: /// </summary>
246: /// <param name="stream">The stream to read data from</param>
247: public static byte[] ReadFully(Stream stream)
248: { 249: byte[] buffer = new byte[32768];
250: using (MemoryStream ms = new MemoryStream())
251: { 252: while (true)
253: { 254: int read = stream.Read(buffer, 0, buffer.Length);
255: if (read <= 0)
256: return ms.ToArray();
257: ms.Write(buffer, 0, read);
258: }
259: }
260: }
261:
262: public bool IsReusable { 263: get { 264: return false;
265: }
266: }
267:
268: }