Files
ayanova7/source/bizobjects/AyaLib/GZTW.AyaNova.BLL/AySignature.cs
2018-06-29 19:47:36 +00:00

367 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using CSLA;
using System.IO;
namespace GZTW.AyaNova.BLL
{
/// <summary>
/// AyaNova captures and stores signatures as vector data,
/// this class is used to parse the captured signature data
/// for various admin info stored with it as well as generate
/// an image for use in reporting or displaying in UI.
/// </summary>
[Serializable]
public class AySignature
{
private string mSignatureCode = "";
/// <summary>
/// Raw signature code stored by client software as captured
/// </summary>
public string SignatureCode {
get
{
return mSignatureCode;
}
set
{
if (mSignatureCode != value)
{
mSignatureCode = value;
Parse();
}
}
}
/// <summary>
/// Signature polyline stroke line paths in x,y coordinate pairs.
/// </summary>
public string StrokePath { get; set; }
/// <summary>
/// Width of signature pade area in pixels
/// Defined because different devices may require different
/// sized signature areas
/// </summary>
public int Width { get; set; }
/// <summary>
/// Height of signature pade area in pixels
/// Defined because different devices may require different
/// sized signature areas
/// </summary>
public int Height { get; set; }
/// <summary>
/// Date time at client device at moment captured signature was started drawing by end user
/// </summary>
public DateTime ClientCapturedDateTime { get; set; }
/// <summary>
/// Date time at server at moment captured signature was saved
/// </summary>
public SmartDate HostCapturedDateTime { get; set; }
/// <summary>
/// Version of signature code schema in case new features or SignatureCode changes are required in future and
/// </summary>
public string Version { get; set; }
/// <summary>
/// Strokepath is not empty and contains at least two sets of points
/// </summary>
public bool HasSignature
{
get
{
if (string.IsNullOrWhiteSpace(StrokePath)) return false;
int nOccurs = AyaBizUtils.StringOccurrences(StrokePath, ",");
return (nOccurs > 1);
}
}
/// <summary>
/// Strokepath is not empty
/// (This is obviously not definitive, but a strong indication the user drew more than a dot or a short line,
/// Of course nothing can certify it's a valid legal signature other than a signature expert or witnesses. :) )
/// </summary>
public bool IsALikelySignature
{
get
{
if (string.IsNullOrWhiteSpace(StrokePath)) return false;
int nOccurs = AyaBizUtils.StringOccurrences(StrokePath, ",");
return (nOccurs > 1);
}
}
Bitmap bmSig = null;
/// <summary>
/// Converts strokepath to bitmap object
/// same size as signature box used to sign
/// in first place.
/// </summary>
public Bitmap SignatureBitmap
{
get
{
//case 1704
if (bmSig == null && HasSignature)
GenerateBitmap();
return bmSig;
}
}
/// <summary>
/// SignatureBitmap converted to byte array which is
/// a more friendly format for some uses
/// </summary>
public byte[] SignatureBitmapAsByteArray
{
get
{
//case 1704
if (!HasSignature) return null;
MemoryStream ms = new MemoryStream();
SignatureBitmap.Save(ms, ImageFormat.Bmp);
return ms.ToArray();
}
}
/// <summary>
/// Constructor takes the signature code
/// containing the dimensions, client datetime captured,
/// version and signature poly line strokes
/// and parses it out.
/// </summary>
/// <param name="signatureCode"></param>
public AySignature(string signatureCode)
{
Clear();
SignatureCode = signatureCode;
if(SignatureCode!=null && !string.IsNullOrWhiteSpace(SignatureCode))
Parse();
}
/// <summary>
/// Constructor
/// </summary>
public AySignature()
{
Clear();
}
/// <summary>
/// Reset signature object to empty
/// </summary>
public void Clear()
{
//defaults in case signature blank as it often will be for most people
SignatureCode = "";
Version = "1";
Width = 300;
Height = 100;
ClientCapturedDateTime = DateTime.MinValue;
StrokePath = "";
HostCapturedDateTime = new SmartDate();
//case 1626
if (bmSig != null)
{
bmSig.Dispose();
bmSig = null;
}
}
private void Parse()
{
Parse2();
return;
//Match m = AyaBizUtils.rxSignatureParser.Match(SignatureCode);
//if (m.Groups.Count < 10) return;
//Version = m.Groups["version"].Value;
//Height = int.Parse(m.Groups["height"].Value);
//Width = int.Parse(m.Groups["width"].Value);
//ClientCapturedDateTime = new DateTime(
// int.Parse(m.Groups["year"].Value),
// int.Parse(m.Groups["month"].Value),
// int.Parse(m.Groups["day"].Value),
// int.Parse(m.Groups["hour"].Value),
// int.Parse(m.Groups["minute"].Value),
// int.Parse(m.Groups["second"].Value)
// );
//StrokePath = m.Groups["strokes"].Value.TrimStart('X').Trim();
////case 1978
//StrokePath = StrokePath.Replace("NaN", "0");
}
//case
private void Parse2()
{
if (String.IsNullOrWhiteSpace(SignatureCode)) return;
Version = "1";//it's always 1
int nWidthStart = SignatureCode.LastIndexOf("width=");
int nHeightStart = SignatureCode.LastIndexOf("height=");
int nCapturedStart = SignatureCode.LastIndexOf("captured=");
int nCloseBracketStart = SignatureCode.LastIndexOf("}");
string sWidthValue = SignatureCode.Substring(nWidthStart + 6, nHeightStart - (nWidthStart + 6));
string sHeightValue = SignatureCode.Substring(nHeightStart + 7, nCapturedStart - (nHeightStart + 7));
string sCapturedValue = SignatureCode.Substring(nCapturedStart + 9, nCloseBracketStart - (nCapturedStart + 9));
string sStrokes = SignatureCode.Substring(nCloseBracketStart + 1).TrimStart('X').Trim().Replace("NaN", "0");
//in case there are commas in the decimal separator
sHeightValue=sHeightValue.Replace(',', '.');
sWidthValue = sWidthValue.Replace(',', '.');
Height = ParseInt(sHeightValue);
Width = ParseInt(sWidthValue);
StrokePath = sStrokes;
string[] cap = sCapturedValue.Split(':');
ClientCapturedDateTime = new DateTime(
int.Parse(cap[0]),//year
int.Parse(cap[1]),//month
int.Parse(cap[2]),//day
int.Parse(cap[3]),//hr
int.Parse(cap[4]),//min
int.Parse(cap[5])//sec
);
//{version=1 width=300.4 height=100.4 captured=2017:2:1:12:27:37}X 62,43 62,45 62,46 63,47 64,49 66,50 66,50 68,50 70,51 75,51 83,51 95,49 110,43 144,26 151,22 156,19 158,18 160,17 160,18 160,22 160,29 162,34 162,39 163,43 166,47 167,51 168,53 171,54 174,57 176,57 179,58 183,59 188,59 194,59 202,59 207,58 214,58 218,58 220,58 222,58 223,59 226,59 232,58 240,57 246,53 252,49 258,45 260,41 262,37 263,37 263,35 263,37 263,38 263,39 263,41 263,42 263,43 263,45 263,45
}
private int ParseInt(string s, bool alwaysRoundDown = false)
{
//converts null/empty strings to zero
if (string.IsNullOrEmpty(s)) return 0;
if (!s.Contains(".")) return int.Parse(s);
string[] parts = s.Split('.');
int i = int.Parse(parts[0]);
if (alwaysRoundDown || parts.Length == 1) return i;
String afterPoint = parts[1];
if (afterPoint.Length > 1)
afterPoint = afterPoint.Substring(0, 1);
int digitAfterPoint = int.Parse(afterPoint);
return (digitAfterPoint < 5) ? i : i + 1;
}
/// <summary>
/// convert stroke path to bitmap
/// </summary>
private void GenerateBitmap()
{
//case 1704
if (!HasSignature) return;
bmSig = new Bitmap(Width, Height);
Graphics g = Graphics.FromImage(bmSig);
//Make the bitmap all white before drawing the signature on it
Brush bWhite = new SolidBrush(Color.White);
g.FillRectangle(bWhite, 0, 0, Width, Height);
//Pen for drawing sample
Pen penDraw = new Pen(Color.Black, 3);
// GraphicsPath pth = new GraphicsPath();
List<Point> lPoints = new List<Point>();
string[] sLines = StrokePath.Split('X');
foreach (string sLine in sLines)
{
string[] sPoints = sLine.Trim().Split(' ');
if (sPoints.Length > 0)
{
lPoints.Clear();
GraphicsPath pth = new GraphicsPath();
foreach (string s in sPoints)
{
lPoints.Add(StringToPoint(s));
}
pth.AddLines(lPoints.ToArray());
g.DrawPath(penDraw, pth);
pth.Dispose();
pth = null;
}
}
if (g != null)
g.Dispose();
}
private static Point StringToPoint(string s)
{
if (string.IsNullOrWhiteSpace(s))
return new Point(0, 0);
if (!s.Contains(','))
return new Point(0, 0);
string[] spoint = s.Split(',');
//case 1939 - can't treat the input in the parse as integers as they can apparently contain decimals now
//I suspect that the conversion with decimal is likely slower than checking to see if it has a decimal first so
//I'm breaking this out into two separate streams for performance
if (s.Contains('.'))
{
// if it contains a decimal then convert differently
return new Point(
Convert.ToInt32(Math.Round(Convert.ToDouble(spoint[0]))),
Convert.ToInt32(Math.Round(Convert.ToDouble(spoint[1])))
);
}
else
{
//if it doesn't contain a decimal then use the original method
//case 1978 wrap this in a try catch block due to one customer having NaN values
//in their signature stroke path string.
try
{
return new Point(int.Parse(spoint[0]), int.Parse(spoint[1]));
}
catch (System.FormatException )
{
return new Point(0, 0);
}
}
}
}//End of class
}//End of namespace