Asked by:
Loading a Signature Captured into an ASP.Net Web Page...

Question
-
User-1975938688 posted
I have a mobile device that i developed software to capture a signature from customers when an item is delivered. The signature is captured as a bitmap and saved to the database as raw bytes. The problem I am having is when loading the signature into an ASP.net web page for viewing. I can't seem to figure out how to translate it back into a viewable image. Instead it just gives me a blank picture box. I used the DevBuzz tutorial on making a vb.net signature capture control, which works very well for my devices. Can anyone help me with getting a viewable image onto the webpage?
I am trying to display the signature image in a gridview as a list of confirmations. I am using a Handler.ashx to handle the population of the images in the gridview. I can post the signature capture code if anyone thinks it will help in figuring out how to load it into the webpage.
Wednesday, February 9, 2011 10:30 AM
All replies
-
User-1975938688 posted
Also as additional information I have tried also navigating to the handler and passing the query string and I get binary output at the top of the screen. I would think that I would get an image...or atleast since I am getting some binary at the top of the page I figure I would get a viewable image when sent to the image control....
For example when navigating to the Handler page passing a query string for an image in the database I get: signatureÝzH<�+7.*A
Could the problem be that it is passing also the column name? If so how can I drop that from the response?
Wednesday, February 9, 2011 3:32 PM -
User-1975938688 posted
Ok so below is the code i use on the device to upload the signature image. I have tried the BinaryWrite function from both the Handler and from a basic webpage passing a query string for the ID of the signature image but i am not getting any results. All i get from the database is the raw binary string representing the signature image. How can i turn it back into an image and display it in my asp.net webpage... This is getting very frustrating... :|
using Microsoft.VisualBasic; using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Windows.Forms; using System.Runtime.InteropServices; using System.IO; namespace Groves_Driver_Pad { public class SignatureControl : Control { // gdi objects private Bitmap _bmp; private Graphics _graphics; private Pen _pen = new Pen(Color.Brown); // list of line segments private ArrayList _lines = new ArrayList(); // the current line segment private ArrayList _points = new ArrayList(); private Point _lastPoint = new Point(0, 0); // if drawing signature or not private bool _collectPoints = false; // notify parent that line segment was updated public event EventHandler SignatureUpdate; // List of signature line segments. public ArrayList Lines { get { return _lines; } } public Bitmap Bmp { get { return _bmp; } } // Return the signature flattened to a stream of bytes. public byte[] SignatureBits { get { return SignatureData.GetBytes(this.Width, this.Height, _lines); } } public SignatureControl() { } protected override void OnPaint(PaintEventArgs e) { // blit the memory bitmap to the screen // we draw on the memory bitmap on mousemove so there // is nothing else to draw at this time (the memory // bitmap already contains all of the lines) //MyBase.OnPaint(e) CreateGdiObjects(); e.Graphics.DrawImage(_bmp, 0, 0); } protected override void OnPaintBackground(PaintEventArgs e) { // don't pass to base since we paint everything, avoid flashing //MyBase.OnPaintBackground(e) } protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); // process if currently drawing signature if (!_collectPoints) { // start collecting points _collectPoints = true; // use current mouse click as the first point _lastPoint.X = e.X; _lastPoint.Y = e.Y; // this is the first point in the list _points.Clear(); _points.Add(_lastPoint); } } protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); // process if drawing signature if (_collectPoints) { // stop collecting points _collectPoints = false; // add current line to list of segments Point[] points = new Point[_points.Count]; int i = 0; for (i = 0; i <= _points.Count - 1; i++) { Point pt = (Point)_points[i]; points[i].X = pt.X; points[i].Y = pt.Y; } _lines.Add(points); // start over with a new line _points.Clear(); // notify container a new segment was added RaiseSignatureUpdateEvent(); } } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); // process if drawing signature if (_collectPoints) { // add point to current line segment _points.Add(new Point(e.X, e.Y)); // draw the new segment on the memory bitmap _graphics.DrawLine(_pen, _lastPoint.X, _lastPoint.Y, e.X, e.Y); // update the current position _lastPoint.X = e.X; _lastPoint.Y = e.Y; // display the updated bitmap Invalidate(); } } // Clear the signature. public void Clear() { _lines.Clear(); InitMemoryBitmap(); Invalidate(); } // Create any GDI objects required to draw signature. private void CreateGdiObjects() { // only create if don't have one or the size changed if (_bmp == null) { InitMemoryBitmap(); } else if (_bmp.Width != this.Bmp.Width | _bmp.Height != this.Bmp.Height) { InitMemoryBitmap(); } } // Create a memory bitmap that is used to draw the signature. private void InitMemoryBitmap() { // load the background image _bmp = new Bitmap(Properties.Resources.sign_here); // get graphics object now to make drawing during mousemove faster _graphics = Graphics.FromImage(_bmp); } // Notify container that a line segment has been added. private void RaiseSignatureUpdateEvent() { if (SignatureUpdate != null) { SignatureUpdate(this, EventArgs.Empty); } } } public class SignatureData { // this identifies this class, used when sent over socket // so receiving application can validate the data stream public const string SignatureId = "signature"; // width of signature canvas private int _width; // height of signature canvas private int _height; // list of line segments private ArrayList _lines; // // Properties // public ArrayList Lines { get { return _lines; } } public int Width { get { return _width; } } public int Height { get { return _height; } } // construct an object from stream of bytes public SignatureData(byte[] bits) { // index into data stream int bitsIndex = 0; // get signature id if (!IsValidStream(bits, ref bitsIndex)) { // this is not what we are expecting throw new Exception("Invalid data stream."); } // width and height _width = Network.GetInt32(bits, ref bitsIndex); _height = Network.GetInt32(bits, ref bitsIndex); // number of line segments Int32 linesCount = Network.GetInt32(bits, ref bitsIndex); _lines = new ArrayList(linesCount); // loop through each line segment and get points int line = 0; for (line = 0; line <= linesCount - 1; line++) { // number of points in this segment Int32 pointsCount = Network.GetInt32(bits, ref bitsIndex); Point[] points = new Point[pointsCount]; // get all points in this segment int point = 0; for (point = 0; point <= pointsCount - 1; point++) { points[point].X = Network.GetInt32(bits, ref bitsIndex); points[point].Y = Network.GetInt32(bits, ref bitsIndex); } // add line segment to list _lines.Add(points); } } // Flatten object to a stream of bytes. public static byte[] GetBytes(int width, int height, ArrayList lines) { // hold byte stream MemoryStream stream = new MemoryStream(); // signature id Network.WriteString(stream, SignatureData.SignatureId); // width and height Network.WriteInt32(stream, width); Network.WriteInt32(stream, height); // number of segments Network.WriteInt32(stream, lines.Count); // each segment //Point[] points = null; foreach (Point[] points in lines) { // points in the segment Network.WriteInt32(stream, points.Length); //Point pt = default(Point); foreach (Point pt in points) { Network.WriteInt32(stream, pt.X); Network.WriteInt32(stream, pt.Y); } } return stream.ToArray(); } // Return true if byte array is a valid SignatureData class, // otherwise return false. private bool IsValidStream(byte[] bits, ref int bitsIndex) { bool valid = false; // see if first value is length of signature id Int32 idLength = Network.GetInt32(bits, ref bitsIndex); if (idLength == SignatureData.SignatureId.Length) { // get the signature id mark byte[] id = Network.GetBytes(bits, ref bitsIndex, idLength); // see if this is the stream we expect if (ASCIIEncoding.ASCII.GetString(id, 0, idLength) == SignatureData.SignatureId) { valid = true; } } return valid; } } // Network helper functions. public class Network { // all static methods private Network() { } // Write int bytes to byte stream. public static void WriteInt32(MemoryStream stream, Int32 data) { stream.Write(BitConverter.GetBytes(data), 0, Marshal.SizeOf(data)); } // Write string to byte stream. First write length, followed // by string content. public static void WriteString(MemoryStream stream, string data) { // write length of string followed by the string bytes WriteInt32(stream, data.Length); stream.Write(ASCIIEncoding.ASCII.GetBytes(data), 0, data.Length); } // Return int from byte stream. public static Int32 GetInt32(byte[] bits, ref int bitsIndex) { Int32 data = BitConverter.ToInt32(bits, bitsIndex); bitsIndex += Marshal.SizeOf(data); return data; } // Return specified bytes from byte stream. public static byte[] GetBytes(byte[] bits, ref int bitsIndex, int length) { byte[] data = new byte[length + 1]; Buffer.BlockCopy(bits, bitsIndex, data, 0, length); bitsIndex += length; return data; } } }
Thursday, February 10, 2011 10:26 AM -
User-1975938688 posted
I am getting closer to solving my problem. Now I have added the above code as a class to my website, and on a sample page i have added code to load the signature image:
Imports System.Data.SqlClient Imports Groves_Driver_Pad Imports System.Drawing Partial Class SignatureTest Inherits System.Web.UI.Page Private WithEvents _signature As SignatureControl = New SignatureControl Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Try Dim strSQL As String = "SELECT Signature, SignName FROM DrivingDirections WHERE ID = @ID" Dim myConn As New SqlConnection(ConfigurationManager.ConnectionStrings("TicketTrackerConnectionString").ConnectionString) Dim myCommand As New SqlCommand(strSQL, myConn) Dim param As SqlParameter = Nothing param = New SqlParameter("@ID", Data.SqlDbType.Int) param.Value = Request.QueryString("ID").ToString() myCommand.Parameters.Add(param) myConn.Open() Dim myReader As SqlDataReader = myCommand.ExecuteReader() If myReader.Read() Then If Not myReader("Signature") Is DBNull.Value Then Dim stream As System.IO.MemoryStream = New System.IO.MemoryStream(CType(myReader("Signature"), Byte())) Dim _sigData As New SignatureData(stream.ToArray) Response.Write(myReader("SignName").ToString()) redrawSig(_sigData) End If ' If Not myReader("Signature") Is DBNull.Value Then myReader.Close() End If ' If myReader.Read Then myConn.Close() Catch ex As Exception Response.Write("Error: " & ex.Message) End Try End Sub Private Sub redrawSig(ByVal thisSigData As SignatureData) Dim _graphics As Graphics _graphics = Graphics.FromImage(_signature.Bmp) DrawSignature(_graphics, thisSigData) _signature.Invalidate() End Sub Private Sub DrawSignature(ByVal g As Graphics, ByVal thisSigData As SignatureData) ' background 'g.Clear(Color.Cornsilk) 'Dim borderPen As New Pen(Color.Brown) Dim sigPen As New Pen(Color.Firebrick) ' border 'g.DrawRectangle(borderPen, 0, 0, 220 - 1, 120 - 1) ' return if don't have a signature If thisSigData Is Nothing Then Return End If If thisSigData.Width = 0 Or thisSigData.Height = 0 Then Return End If ' draw each line segment Dim line As Point() For Each line In thisSigData.Lines If Not line Is Nothing Then If line.Length > 0 Then ' draw lines or curves g.DrawLines(sigPen, line) End If End If Next line End Sub End Class
However now I am getting this error: Error: Value cannot be null. Parameter name: imageI am not sure how the image can be null since there is binary data at the location of the ID that I am passing to the page...Does anyone have any ideas?
Update: I know there is binary data at the id location because I can do Response.BinaryWrite and see the binary characters at the top of the page instead of an error...
Thursday, February 10, 2011 11:48 AM -
User-1975938688 posted
No suggestions, ideas or recommendations? I am really stumped here. I can get the signature to reload on the mobile device but not the web page. I am not sure why it works on the device but not the webpage. I wonder if I need to convert the control into a web control? Any Ideas?
Friday, February 11, 2011 9:31 AM -
User-179775383 posted
Hi,
I am wondering if you have got any solution to this problem? becuase i am having a same problem.
Friday, November 25, 2011 11:44 AM -
User-1975938688 posted
Yes I did solve it. You have to specify the file format when trying to display it. In my case it was jpg.Friday, November 25, 2011 12:01 PM -
User-179775383 posted
Hi,
Could you pls copy your code here?
Friday, November 25, 2011 12:52 PM