将3字节ascii签名格式转换为图像

时间:2011-10-24 19:04:07

标签: .net decode

我正在使用Ingenico i6580 pinpad机器,并以默认的3字节ascii签名格式检索客户的签名。我使用VS2010在VB.NET中编码。有没有人知道是否有可以将此格式转换回图像的实用程序?

2 个答案:

答案 0 :(得分:0)

我正在使用这个C#函数在PictureEdit(一个DevExpress控件)上绘制图像

 public void getSignGraph(string signatureData)
    {
        var stringOfPoints = signatureData.Split(',');
        var beginStroke = true;
        //PictureEdit1 is a DevExpress control where the image will be drawn
        using (Graphics g = PictureEdit1.CreateGraphics())
        {
            g.Clear(Color.White);
            var keepx = "";
            var keepy = "";
            foreach (var pointval in stringOfPoints)
            {
                if (!String.IsNullOrEmpty(pointval))
                {
                    var point = pointval.Split(',');
                    var x = point[0];
                    var y = point[1];

                    if (x == "#" && y == "#")
                    {
                        beginStroke = true;
                    }
                    else
                    {
                        if (beginStroke)
                        {
                            beginStroke = false;
                        }
                        else
                        {
                            g.DrawLine(new Pen(Color.Black), new Point(Convert.ToInt32(keepx), Convert.ToInt32(keepy)), new Point(Convert.ToInt32(x), Convert.ToInt32(y)));
                        }
                    }
                    keepx = x;
                    keepy = y;
                }
            }
        }
    }

答案 1 :(得分:0)

我看到这个帖子很旧了,但我希望这对未来的人有所帮助。

可以在 here 中找到 SigBin2 格式的文档。

此外,this forum post 在解决这个问题时对我很有帮助。

这是我将 SigBin2 签名渲染到图像的最终代码:

private Bitmap sigBinToImage(string sigBin2Source, float scaleFactor)
{
    // Compute all the points for each of the segments in the signature
    List<List<Point>> pointLists = sigBinToPoints(sigBin2Source);

    // Draw the signature
    Bitmap signatureImage = pointsToImage(pointLists, scaleFactor);

    return signatureImage;
}

private List<List<Point>> sigBinToPoints(string sigBin2Source)
{
    /*
     * The SigBin2 format (also called 3-byte Ascii format) is a relative coordinate format.
     * The format is structured as follows:
     *   1) Segment Start control character
     *        An ascii character in the range 96-111 containing bits used to determine the
     *        starting position of the line segment (pen stroke).
     *        
     *   2) Coordinate Data Character 1
     *        An ascii character in the range 32-95 containing bits used to determine the
     *        X-offset from the last coordinate.
     *        Note that the ascii value has to be shifted down by 32 before being used.
     *        
     *   3) Coordinate Data Character 2
     *        An ascii character in the range 32-95 containing bits used to determine the
     *        Y-offset from the last coordinate
     *        Note that the ascii value has to be shifted down by 32 before being used.
     *        
     *   4) Coordinate Data Character 3
     *        An ascii character in the range 32-95 containing bits used to determine the
     *        remaining bits of the X-offset and Y-offset from the last coordinate
     *        Note that the ascii value has to be shifted down by 32 before being used.
     *        
     *   *) Items (2), (3), and (4) are repeated for all datapoints in the line segment (pen stroke).
     *   
     *   5) Pen Up control character
     *        An ascii character (112) signaling that the line segment (pen stroke) has ended
     *        
     *   *) Items (1) through (5) are repeated for all line segments (pen strokes)
     *   
     * More information about this format can be found here:
     *   https://forums.macrumors.com/attachments/3-byte-ascii-doc.295002/
     */


    short cX = 0, cY = 0;               // Absolute coordinate of last datapoint
    short dX = 0, dY = 0;               // Offset from last datapoint
    int parseMode = 0;                  // The type of character we are expecting next
    bool firstPointInSegment = false;   // Disables sign-extension for the first coordinate in the segment

    List<List<Point>> points = new List<List<Point>>();

    // Loop through all characters in the SigBin2 string
    for (int i = 0; i < sigBin2Source.Length; i++)
    {
        short asciiValue = (short)sigBin2Source[i];
        switch (asciiValue)
        {
            case object _ when asciiValue >= 0x60 && asciiValue <= 0x6F:   // Segment Start control character
                if (parseMode == 0)
                {
                    // Get the start position bits (b3 = X10, b2 = X9, b1 = Y10, b0 = Y9)
                    cX = (short)((asciiValue & 0xC) << 7);
                    cY = (short)((asciiValue & 0x3) << 9);

                    // Look for the first byte of the 3-byte coordinate
                    parseMode = 1;

                    // Ignore relative offset for the first point of the sequence
                    firstPointInSegment = true;

                    // Allocate new list for this segment's points
                    points.Add(new List<Point>());
                }
                else
                {
                    // Segment Start character is in an invalid position (a Pen Up character needs to precede it)
                    throw new Exception($"Illegal Segment Start character '{sigBin2Source[i].ToString()}' at position {i.ToString()}");
                }

                break;

            case object _ when asciiValue >= 0x20 && asciiValue <= 0x5F:   // Part of a 3-byte coordinate
                // Check which byte of the 3-byte coordinate that this is
                // Note: ascii values are offset by 0x20
                if (parseMode == 1)   // First byte
                {
                    // Get X-bits 3 to 8 (bits 0 to 5 of asciiValue)
                    dX = (short)((asciiValue - 0x20) << 3);

                    // Look for the second byte
                    parseMode = 2;
                }
                else if (parseMode == 2)   // Second byte
                {
                    // Get Y-bits 3 to 8 (bits 0 to 5 of asciiValue)
                    dY = (short)((asciiValue - 0x20) << 3);

                    // Look for the third byte
                    parseMode = 3;
                }
                else if (parseMode == 3)   // Third byte
                {
                    // Mask bits 0 to 5 (for safety) and shift ascii value
                    asciiValue = (short)((asciiValue - 0x20) & 0x3F);

                    // Get X bits 0 to 2 (bits 3 to 5 of asciiValue)
                    dX = (short)(dX | ((asciiValue >> 3) & 0x7));

                    // Get Y bits 0 to 2 (bits 0 to 2 of asciiValue)
                    dY = (short)(dY | (asciiValue & 0x7));

                    // Sign extend
                    if (!firstPointInSegment)   // Don't sign-extend offsets for the first point in the segment
                    {
                        // If sign bit is 1, sign-extend to bits 9-15 to make it a negative short
                        if (dX > 255)
                            dX = (short)(dX | 0xFE00);
                        if (dY > 255)
                            dY = (short)(dY | 0xFE00);
                    }
                    else
                    {
                        firstPointInSegment = false;
                    }

                    // Add the coordinate offset to the last absolute coordinate
                    cX += dX;
                    cY += dY;

                    // Add this point to the list
                    points[points.Count - 1].Add(new Point(cX, cY));

                    // Assume the next character will be the first byte of another 3-byte coordinate
                    parseMode = 1;
                }
                else
                {
                    // Coordinate character is in invalid position (a Segment Start character needs to preceed it)
                    throw new Exception($"Illegal Coordinate character '{sigBin2Source[i].ToString()}' at position {i.ToString()}");
                }

                break;

            case 0x70:   // Pen Up control character
                if (parseMode == 1)
                {
                    // Look for the next Segment Start control character
                    parseMode = 0;
                }
                else
                {
                    // Pen Up character is in invalid position (the preceding coordinate needs to be complete)
                    throw new Exception($"Illegal Pen Up character '{sigBin2Source[i].ToString()}' at position {i.ToString()}");
                }

                break;

            default:   // Unrecognized character
                throw new Exception($"Unrecognized character '{sigBin2Source[i].ToString()}' at position {i.ToString()} (mode: {parseMode.ToString()})");
        }
    }

    // Return the list of coordinate points
    return points;
}

private Bitmap pointsToImage(List<List<Point>> pointLists, float scaleFactor)
{
    // Find the max and min points
    int minX = int.MaxValue, maxX = int.MinValue, minY = int.MaxValue, maxY = int.MinValue;
    foreach (List<Point> pointList in pointLists) {
        foreach (Point p in pointList)
        {
            if (p.X > maxX)
                maxX = p.X;
            if (p.X < minX)
                minX = p.X;
            if (p.Y > maxY)
                maxY = p.Y;
            if (p.Y < minY)
                minY = p.Y;
        }
    }

    // Create a bitmap from the computed dimensions (with some padding around the edges)
    int edgePaddingBase = 5;
    int edgePadding = (int)((float)edgePaddingBase * scaleFactor);
    int imgWidth = (int)((float)(maxX - minX) * scaleFactor) + (edgePadding * 2);
    int imgHeight = (int)((float)(maxY - minY) * scaleFactor) + (edgePadding * 2);
    Bitmap signatureImg = new Bitmap(imgWidth, imgHeight);

    // Setup drawing settings
    //   Note that the Y-axis is flipped
    Graphics g = Graphics.FromImage(signatureImg);
    g.ScaleTransform(scaleFactor, -1F * scaleFactor);
    g.TranslateTransform((-1 * minX) + edgePaddingBase, (-1 * minY) + -1.0F * (maxY - minY) - edgePaddingBase);
    g.Clear(Color.White);

    // Draw each of the segments on the image
    foreach (List<Point> pointList in pointLists)
        g.DrawLines(Pens.Black, pointList.ToArray<Point>());

    g.Dispose();

    // Return the signature image
    return signatureImg;
}

需要注意的一件事是确保在处理之前取消转义 SigBin 字符串。如果您在 JSON 文件中收到签名数据,则 SigBin 字符串很可能会被转义。例如,在 JSON 中,引号 " 用 \" 表示,因此您必须确保字符串在处理之前未转义。 This 页面对于取消转义 SigBin 字符串很有用。但是,请注意,此工具有时会将事情解释为实际上并非旨在进行转义的转义。如果您在我的代码中遇到“非法字符位置”异常,则很可能是转义问题。