8

我正在开发一个移动应用程序(离子),其中有一个用户签名字段。用户需要用手指在画布上签名。

现在这个应用程序有网络版本,他们使用 topaz sigweb 来做到这一点。现在要保存签名并在两端查看,我需要某种转换。

我检查了 sigweb 的文档,他们以 ASCHII 十六进制格式保存签名。我不确定他们是否使用任何类型的内部加密,因为我无法将十六进制字符串转换为任何有效的 base 64。

我不确定是否有其他方法可以做到这一点。如果有人有任何想法,请分享。

提前致谢。

4

3 回答 3

3

我意识到这有点老了,但我敢肯定还有很多其他人有同样的问题。

当我需要在不需要插件或安装的标准桌面或移动浏览器中向最终用户显示签名时,我遇到了完全相同的问题。与 Topaz 交谈,可以选择使用 PHP 组件对象模型在服务器端运行 ActiveX 来创建图像。不幸的是,这只适用于 Windows 服务器并且需要大量的摆弄。我只设法让它在我的测试台上工作,它对我的​​ linux 生产服务器毫无用处。我再次联系了 Topaz,他们说唯一的另一个选择是 Java 小程序!?

活动X!Java小程序!这是2019年,不是2009年!(对不起,我在那里过量使用感叹号,但 Jeez)。

最后我决定自己尝试解码,最终想出了这个 PHP 函数来创建一个 SVG。他们使用的格式是专有的(实际上有点乱七八糟),但本质上它只是十六进制、坐标和笔划长度——因此对于某人来说,将以下内容转换为任何其他平台应该很容易。

//Requires $SigString and accepts optional filename.
//If filename is supplied the image will be written to that file
//If no filename is supplied the SVG image will be returned as a string which can be echoed out directly

function sigstring2svg($SigString, $filename = NULL)
{
    $raw = hex2bin($SigString); //Convert Hex
    $arr = explode(PHP_EOL, $raw); //Split into array
    if ($arr[1] > 0) { //Check if signature is empty
        $coords = array_slice($arr, 2, $arr[0]); //Separate off coordinate pairs
        $lines = array_slice($arr, ($arr[0] + 2), $arr[1]); //Separate off number of coordinates pairs per stroke
        if ($arr[1] == 1) {
            $lines[] = ($arr[0] + 2); //If there is only 1 line the end has to be marked
        }
        $done = 0;
        foreach ($lines as $line => $linevalue) {
            if ($linevalue > $done) {
                $strokes[$line] = array_slice($coords, $done, $linevalue); //Split coordinate pairs into separate strokes
            }
            $done = $linevalue;
        }
        //Split X and Y to calculate the maximum and minimum coordinates on both axis
        $xmax = 0;
        $xmin = 999999;
        $ymax = 0;
        $ymin = 999999;
        foreach ($strokes as $stroke => $xycoords) {
            foreach ($xycoords as $xycoord) {
                $xyc = explode(' ', $xycoord);
                $xy[$stroke]['x'][] = $xyc[0];
                if ($xyc[0] > $xmax) $xmax = $xyc[0];
                if ($xyc[0] < $xmin) $xmin = $xyc[0];
                $xy[$stroke]['y'][] = $xyc[1];
                if ($xyc[1] > $ymax) $ymax = $xyc[1];
                if ($xyc[1] < $ymin) $ymin = $xyc[1];
            }
        }
        //Add in 10 pixel border to allow for stroke
        $xmax += 10;
        $xmin -= 10;
        $ymax += 10;
        $ymin -= 10;
        //Calculate the canvas size and offset out anything below the minimum value to trim whitespace from top and left
        $xmax -= $xmin;
        $ymax -= $ymin;

        //Iterate through each stroke and each coordinate pair to make the points on the stroke to build each polyline as a string array
        foreach ($xy as $lines => $axis) {
            $polylines[$lines] = '<polyline class="sig" points="';
            foreach ($xy[$lines]['x'] as $point => $val) {
                $x = $xy[$lines]['x'][$point];
                $y = $xy[$lines]['y'][$point];
                $polylines[$lines] .= ($x - $xmin) . ',' . ($y - $ymin) . ' ';
            }
            $polylines[$lines] .= '"/>';
        }
        //Build SVG image string
        $image = '
    <svg id="sig" data-name="sig" xmlns="http://www.w3.org/2000/svg" width="' . $xmax . '" height="' . $ymax . '" viewBox="0 0 ' . $xmax . ' ' . $ymax . '">
      <defs>
        <style>
          .sig {
            fill: none;
            stroke: #000;
            stroke-linecap: round;
            stroke-linejoin: round;
            stroke-width: 4px;
          }
        </style>
      </defs>
      <title>Signature</title>
      <g>
        ';
        foreach ($polylines as $polyline) {
            $image .= $polyline;
        }
        $image .= '
      </g>
    </svg>';

        //If file name is supplied write to file
        if ($filename) {
            try {
                $file = fopen($filename, 'w');
                fwrite($file, $image);
                fclose($file);
                return $filename;
            } catch (Exception $e) {
                return false;
            }
        } else {
            //If file name is not supplied return the SVG image as a string
            return $image;
        }
    } else {
        return "Signature is empty";
    }
}
于 2019-01-13T21:03:43.100 回答
2

在过去的 10 年里,我用 Topaz 的 SigPlus/SigWeb 花了 2 天时间摆弄这段代码,无数小时(甚至几个月)。

当我在这里偶然发现斯科特的答案时,我只需要让它发挥作用。不幸的是,@ScottG 犯了几个关键错误,所以它不能开箱即用,我的同事尝试过它也由于缺乏文档而失败。所以我的回答是 99% 建立在 Scott 的代码和输入之上,但我要补充:

  • 可使用的示例 SigString
  • 需要完成的修复
  • 要求
  • 更多评论
  • SigString 结构解释
  • HTML5 画布的奖励代码

无论如何,由于需要讲述整个故事,所以我将整个示例放在这里,而不仅仅是(固定的)PHP函数。

如果您急于复制/粘贴代码并继续前进 - 跳到实际代码。如果您想了解它的工作原理,请阅读全部内容。

首先我们需要知道这个代码(就像 Scott 的代码一样)只有在 SigString 既没有压缩也没有加密的情况下才有效。如果您有以前的签名数据库,那么是时候将它们转换为未压缩/未加密的了。这是第一步,也是其他人立即放弃 Scott 代码的原因。不幸的是,解释压缩/解压缩超出了这个答案的范围(我达到了字符限制,所以请参阅我的 GitHub)。

这是使用 Topaz 平板电脑生成的示例 SigString,使用 SetSigCompressionMode(1) 保存(“1” = 4:1 比率的无损压缩):

04002900C301E101FFFF00FF00FE00FD01FC00FA01FA02F802F803F704F604F604F604F503F502F402F402F301F300F301F200F300F201F100F200F100F100F202F202F203F303F305F404F504F604F703FA01FD01FF000034000902600000FF01FF01FF01FE010001000101000201030103010502070207030803080408040A060A060B070E080D070D060E060E050E050E050E040F030E030D020E020E010E010D010D010C010B010B010A010A000800070006FF0400040001FF01FF000000000000001B005A02F700FF00FE02FE02FD03FC02FC02FA02F900F801F6FFF500F5FEF5FFF5FEF7FEF8FEF9FEFBFEFCFEFDFEFEFE00FF00FF0000010000004D000803A1010000FF010000FF00FFFEFFFEFEFCFFFCFEFCFEFAFDFAFEF8FDF8FCF7FCF6FBF6FCF6FBF5FCF5FCF5FBF5FDF5FCF5FDF4FCF4FDF5FDF4FDF4FEF5FEF5FFF500F600F501F701F702F804F804F904FA06FB08FB09FD0BFD0DFF0C010D020E020C040E040C050B0509060807060805080308020801080009FF08FE08FD08FC07FB06FB06F805F703F402F301F100F1FEF2FEF6FEFBFEFE000000

这是相同的 SigString 转换为 SetSigCompressionMode(0)(无压缩):

3139370D0A ... <cut - see code below, I've hit a character limit> ... 3132300D0A

奖励:如果你真的需要在磁盘/数据库中节省空间,请使用它而不是内置的 SigWeb/SigPlus 压缩,在代码中有相同的示例 SigString 未压缩,然后使用 PHP 的内置 gzdeflate 函数压缩,你实际上以这种方式获得更小的输出。

现在,我想解释一下 SigString 格式(基于我的示例 SigString)。下面是原始字符串的输出,在 hex2bin 转换之后(为了便于阅读而裁剪):

197
4
451 481
450 480
450 479
450 477
450 474
451 470
451 464
452 458
...
787 229
773 227
763 225
758 223
756 223
756 223
0
41
93
120

请注意,我们实际上在这里有 4 个部分:

  • 第 1 行(示例:197)是我们在签名文件中拥有的坐标或“点”的数量,这些数字对将从第 3 行开始
  • 第 2 行(示例:4)是我们拥有的行数,或者更好地说,我们保留的断点数,这些是文件末尾的单个数字条目(在此示例中:0/41/93/120)
  • 从第 3 行开始,我们拥有所有带有坐标对的 (197) 行,从逻辑上讲,这将与您的签名实际具有的列表一样小或一样长。明确地说,如果第 1 行说“5”,那么您将只有 5 对,如果第 1 行说“2999”,您将拥有 2999 对
  • 在文件末尾,我们终于有了第 2 行枚举的断点或端点列表的段。同样,如果第 2 行说“3”,你的文件末尾将有 3 个单数行,如果行2 说“899”,那么你的文件末尾会有 899 个单数行的巨大列表。

正如你从这个描述中看到的,我们需要阅读前两行才能开始。稍后在 Scott 的代码中,这些将是数组成员,即 arr[0] 和 arr[1]。使用这两个数字,我们可以将签名分成两个数组,一个带有坐标对,另一个带有线条,或者更准确地说 - 线条结尾。您可以通过多种方式对其进行切片,只需记住 Sigstring 布局的格式。

使用此示例,我们首先根据提供的示例 SigString(缩短)将 hex2bin 结果拆分为数组:

["197","4","451 481","450 480", ... ,"758 223","756 223","756 223"]

我们通过创建另一个包含“lines”值的数组来完成切片(同样,只要原始数组的第二个元素说,这个数组的长度就会被剪切):

["0","41","93","120"]

现在,在代码中你会看到我做了一个修复,所以我的实际“lines[]”数组打印了这个:

["0","41","93","120","197"]

请注意,我的代码修复在最后添加了“197”。那不是原始签名的结尾,对吗?但是我们仍然知道,从签名的第一行,我们知道它有 197 个点,所以我只是将其连接到数组中。为什么要这样做?

我们的示例签名是简单的两个字母 - “AP” - 只有 4 行:

  • 坐标 0 到 41
  • 坐标 42 到 93
  • 坐标 94 到 120
  • 和... 121 到 197

最后一个没有清楚地描述,因为它应该很明显,它只是从前一行的结尾 (120 + 1) 到坐标列表的结尾。但是,好吧,取决于您如何执行以下步骤,您的代码可能需要也可能不需要 lines[] 数组中的“197”。为了以最少的更改修复原始代码,我们需要在数组中存在“197”,以便 foreach 循环可以正确写入最后一行。我们可以通过几种明显的方式修复它:

  • 如果它是 for-each 中的最后一个元素,则使用 $done + $arr[0] 执行不同的代码
  • 我们根本不需要“线条”,正如您将在我的额外“HTML5 画布”代码中看到的那样,您实际上并没有像在 SVG 中那样定义行开始/结束,您只需移动铅笔,但有时它会移动“在纸上”有时“在空中”
  • ETC

如前所述,您只需要确保使用所有坐标,并纠正原始代码,这是最简单的方法(单行),无需更多重写。

知道我们现在拥有什么后,Scott 选择了一个包含另一个切片代码的解决方案。他正在将我们的 coords[] 数组分割成单独的线,嗯,折线,然后输出到 SVG。由于 SVG 的格式,这实际上是首选方式,或者至少我在查看 Scott 的做法以及 SVG 作为最终产品在 HTML 中的实际外观时是这样认为的。

但不幸的是,原始代码有 2 个错误(我花了 2 天时间才解决 :))。一个错误是缺少最后一行,并且根据您的示例签名,它可能并不明显。我的第一个样本只有一个点(带有“i”字母的签名,所以签名的人在最后的“i”上做了点,所以它根本不明显,只是少了一个点)。但是我在这里提供的这个“AP”示例实际上将整个字母“P”作为最后一行,所以缺少整个字母就很明显了。无论如何,我解释了我是如何解决这个问题的,所以继续讨论第二个问题。

另一个错误是在切片到线段/折线时,切片长度使用了错误的值。它实际上使用最后一个点对其进行切片,但是 array_slice PHP 函数期望 (array, starting point, length) 而 Scott 可能期望它是 (array, starting point, endpoint)。这在一些签名样本中也很微妙。本质上发生的是一些点被重复使用,但如果你在一行的顶部写一条线,那没关系(特别是在完美的数字绘图中)。但!通过重新使用坐标,它有时会在两条或多条线组合的路径上延伸一条线,基本上否定了“笔”在“空中”时发生的情况,突然间所有签名看起来好像笔从未被移除从纸。例如。“i”上的“点”将是一条连接“

我解决了这个问题,所以现在它会像这样切片(再次基于我的示例 SigString 的输出):

line = 1 , linevalue = 41 , done = 0 , linelength = 41
line = 2 , linevalue = 93 , done = 41 , linelength = 52
line = 3 , linevalue = 120 , done = 93 , linelength = 27
line = 4 , linevalue = 197 , done = 120 , linelength = 77

我在代码中留下了未压缩的 SigString 示例,以及代码中注释掉的一大堆回声,如果您取消注释这些,您将看到整个过程逐步呈现在您面前。如果您对固定代码的部分进行注释/取消注释,您也会看到与 Scott 的版本相同的内容,以便进行比较。

我在我添加的行上发表了评论,如果这是对 Scott 的行的更改,我将他的行评论在我的固定行上方,以便您轻松比较。我没有标记我的所有评论与他的评论,因为无论如何都可以跳过这些评论,我的评论只是补充解释或解释我的更改。

这也是我的 GitHub 存储库的链接,因此您也可以在那里看到整个代码以及完整的文章,因为由于 Stack Overflow 字符限制,我不得不缩短这里的内容:https ://github.com/luxzg/Topaz

如果它不适合您,请在此处发表评论或在 GitHub 上提出问题。

现在终于开始编码了,抱歉花了这么长时间。您可以将整个内容复制/粘贴到http://phpfiddle.org/以查看它的实际效果

--- 将下面的所有内容复制/粘贴到 PHPFIDDLE.ORG ---

<?php

//              My changes marked with comment ->   //  fix by LuxZg 08.05.2020.

        //Requires $SigString and accepts optional filename.
        //$SigString has to be UNCOMPRESSED and UNENCRYPTED //  by LuxZg 08.05.2020.
        //If filename is supplied the image will be written to that file
        //If no filename is supplied the SVG image will be returned as a string which can be echoed out directly

        function sigstring2svg($SigString, $filename = NULL)
        {
            $raw = hex2bin($SigString); //Convert Hex
            $raw = str_ireplace(array("\r",'\r'),'', $raw); //  fix by LuxZg 08.05.2020. - hex2bin generated code with \r\n both being used
                //  so after exploding it, the \r would be left, sometimes causing more bugs, but otherwise unseen unless encoded to eg. JSON

//          print the binary format
//          echo '<br><br>First we echo raw string, after hex2bin conversion:<br><br>';
//          echo '<pre>'.$raw.'</pre>'; //  this didn't show \r\n
//          echo '<pre>'.json_encode($raw).'</pre>';    //  this did show \r\n , and after fix now shows just \n, which is now OK for the next step

            $arr = explode(PHP_EOL, $raw);  //Split into array
            if ($arr[1] > 0) { //Check if signature is empty
                    $coords = array_slice($arr, 2, $arr[0]);    //Separate off coordinate pairs // keep in mind SigString format is: coordinate amount - amount of lines - coordinate pairs - end-points of lines
                                                                                                // also get to know your array_slice format: array name - start of slice - length of slice
                    $lines = array_slice($arr, ($arr[0] + 2), $arr[1]); //Separate off number of coordinates pairs per stroke
                    $lines[] = $arr[0]; // fix by LuxZg - 08.05.2020. - later code needs last coordinate added to array, so last slice/SVG segment isn't ommited by mistake

//                  bunch of echoes below, not needed, except to learn/understand, note that to see \r and/or \n you need to use json_encode, that's why I left both, to debug the error Scott's code had
//                  echo '<br><br>Arr[] values:<br><br>';
//                  echo '<pre>';
//                  print_r(array_values($arr));
//                  print_r(json_encode($arr));
//                  echo '</pre>';
//                  echo '<br><br>Coords[] values:<br><br>';
//                  echo '<pre>';
//                  print_r(array_values($coords));
//                  print_r(json_encode($coords));
//                  echo '</pre>';
//                  echo '<pre>';
//                  echo '<br><br>Lines[] values:<br><br>';
//                  print_r(array_values($lines));
//                  print_r(json_encode($lines));
//                  echo '</pre><br><br>';

                    if ($arr[1] == 1) {
                            $lines[] = ($arr[0] + 2);   //If there is only 1 line the end has to be marked
                        }
                    $done = 0;
//                  we always start at zero, it's first member of array, first coordinate we use

                    foreach ($lines as $line => $linevalue) {
                            if ($linevalue > $done) {
                                    $linelength = $linevalue-$done; //  fix by LuxZg 08.05.2020. - we need to know where slice ends, so we use the "done" of previous line as our new start
                                                                    //  and we know where we need to end, so length is simple math
//                                  $strokes[$line] = array_slice($coords, $done, $linevalue);  //Split coordinate pairs into separate strokes  //  end of line is wrong
                                                                                                //  it was including too many lines/coordinates in each iteration, again and again, so I fixed it, left this for comparison
                                    $strokes[$line] = array_slice($coords, $done, $linelength); //Split coordinate pairs into separate strokes  //  fix by LuxZg 08.05.2020. - end of slice is now length of line, as should be
                                }

//                          just an echo to see what's actually happening and also why we needed to add that one last point in our lines[] array earlier
//                          echo "<br>line = ".$line." , linevalue = ".$linevalue." , done = ".$done." , linelength = ".$linelength."<br>";

                            $done = $linevalue; //  we set new value to $done as next line will start from there
                        }

//              I did not touch anything else in this PHP function, from this point below ! SVG drawing code is great!

                    //Split X and Y to calculate the maximum and minimum coordinates on both axis
                    $xmax = 0;
                    $xmin = 999999;
                    $ymax = 0;
                    $ymin = 999999;
                    foreach ($strokes as $stroke => $xycoords) {
                            foreach ($xycoords as $xycoord) {
                                    $xyc = explode(' ', $xycoord);
                                    $xy[$stroke]['x'][] = $xyc[0];
                                    if ($xyc[0] > $xmax) $xmax = $xyc[0];
                                    if ($xyc[0] < $xmin) $xmin = $xyc[0];
                                    $xy[$stroke]['y'][] = $xyc[1];
                                    if ($xyc[1] > $ymax) $ymax = $xyc[1];
                                    if ($xyc[1] < $ymin) $ymin = $xyc[1];
                            }
                    }
                    //Add in 10 pixel border to allow for stroke
                    $xmax += 10;
                    $xmin -= 10;
                    $ymax += 10;
                    $ymin -= 10;
                    //Calculate the canvas size and offset out anything below the minimum value to trim whitespace from top and left
                    $xmax -= $xmin;
                    $ymax -= $ymin;

                    //Iterate through each stroke and each coordinate pair to make the points on the stroke to build each polyline as a string array
                    foreach ($xy as $lines => $axis) {
                            $polylines[$lines] = '<polyline class="sig" points="';
                            foreach ($xy[$lines]['x'] as $point => $val) {
                                    $x = $xy[$lines]['x'][$point];
                                    $y = $xy[$lines]['y'][$point];
                                    $polylines[$lines] .= ($x - $xmin) . ',' . ($y - $ymin) . ' ';
                            }
                            $polylines[$lines] .= '"/>';
                    }

                    //Build SVG image string
                    $image = '
                        <svg id="sig" data-name="sig" xmlns="http://www.w3.org/2000/svg" width="' . $xmax . '" height="' . $ymax . '" viewBox="0 0 ' . $xmax . ' ' . $ymax . '">
                            <defs>
                                <style>
                                    .sig {
                                        fill: none;
                                        stroke: #000;
                                        stroke-linecap: round;
                                        stroke-linejoin: round;
                                        stroke-width: 4px;
                                    }
                                </style>
                            </defs>
                            <title>Signature</title>
                            <g>
                                ';
                                foreach ($polylines as $polyline) {
                                        $image .= $polyline;
                                }
                                $image .= '
                            </g>
                        </svg>';

                    //If file name is supplied write to file
                    if ($filename) {
                            try {
                                    $file = fopen($filename, 'w');
                                    fwrite($file, $image);
                                    fclose($file);
                                    return $filename;
                            } catch (Exception $e) {
                                    return false;
                            }
                    } else {
                            //If file name is not supplied return the SVG image as a string
                            return $image;
                    }
                } else {
                        return "Signature is empty";
                }
        }

//              OK to complete the example I actually use the function

        //  this is my simple example "AP" signature, all decompressed and ready to be put through function
        $sigdec = '3139370D0A340D0A343531203438310D0A343530203438300D0A343530203437390D0A343530203437370D0A343530203437340D0A343531203437300D0A343531203436340D0A343532203435380D0A343534203435300D0A343536203434320D0A343539203433330D0A343633203432330D0A343637203431330D0A343731203430330D0A343735203339320D0A343738203338310D0A343830203336390D0A343832203335370D0A343834203334340D0A343835203333310D0A343835203331380D0A343836203330340D0A343836203239310D0A343836203237370D0A343837203236320D0A343837203234380D0A343837203233330D0A343837203231380D0A343837203230340D0A343839203139300D0A343931203137360D0A343934203136330D0A343937203135300D0A353032203133380D0A353036203132370D0A353130203131370D0A353134203130380D0A353137203130320D0A3531382039390D0A3531392039380D0A3531392039380D0A3532312039360D0A3532312039350D0A3532322039340D0A3532332039330D0A3532342039310D0A3532352039310D0A3532362039310D0A3532372039320D0A3532372039340D0A3532382039370D0A353239203130300D0A353330203130350D0A353332203131320D0A353334203131390D0A353337203132370D0A353430203133350D0A353434203134330D0A353438203135330D0A353534203136330D0A353630203137340D0A353637203138380D0A353735203230310D0A353832203231340D0A353838203232380D0A353934203234320D0A353939203235360D0A363034203237300D0A363039203238340D0A363133203239390D0A363136203331330D0A363139203332360D0A363231203334300D0A363233203335340D0A363234203336380D0A363235203338310D0A363236203339340D0A363237203430360D0A363238203431370D0A363239203432380D0A363330203433380D0A363331203434380D0A363331203435360D0A363331203436330D0A363331203436390D0A363330203437330D0A363330203437370D0A363330203437380D0A363239203437390D0A363238203437390D0A363238203437390D0A363238203437390D0A363238203437390D0A363032203234370D0A363031203234370D0A353939203234390D0A353937203235310D0A353934203235340D0A353930203235360D0A353836203235380D0A353830203236300D0A353733203236300D0A353635203236310D0A353535203236300D0A353434203236300D0A353333203235380D0A353232203235370D0A353131203235350D0A353032203235330D0A343934203235310D0A343837203234390D0A343832203234370D0A343738203234350D0A343735203234330D0A343733203234310D0A343733203234300D0A343733203233390D0A343733203233390D0A343734203233390D0A343734203233390D0A373736203431370D0A373736203431370D0A373735203431380D0A373735203431380D0A373734203431380D0A373733203431360D0A373732203431340D0A373730203431300D0A373639203430360D0A373637203430320D0A373635203339360D0A373632203339300D0A373630203338320D0A373537203337340D0A373533203336350D0A373439203335350D0A373434203334350D0A373430203333350D0A373335203332340D0A373331203331330D0A373237203330320D0A373232203239310D0A373139203238300D0A373135203236390D0A373132203235370D0A373038203234350D0A373035203233340D0A373032203232320D0A363939203231300D0A363937203139390D0A363935203138380D0A363934203137370D0A363934203136370D0A363934203135360D0A363935203134370D0A363936203133380D0A363938203133300D0A373032203132320D0A373036203131350D0A373130203130390D0A373136203130340D0A3732342039390D0A3733332039360D0A3734342039330D0A3735372039320D0A3736392039330D0A3738322039350D0A3739362039370D0A383038203130310D0A383232203130350D0A383334203131300D0A383435203131350D0A383534203132310D0A383632203132380D0A383638203133360D0A383733203134340D0A383736203135320D0A383738203136300D0A383739203136380D0A383739203137370D0A383738203138350D0A383736203139330D0A383733203230310D0A383639203230380D0A383634203231340D0A383539203232300D0A383531203232350D0A383432203232380D0A383330203233300D0A383137203233310D0A383032203233310D0A373837203232390D0A373733203232370D0A373633203232350D0A373538203232330D0A373536203232330D0A373536203232330D0A300D0A34310D0A39330D0A3132300D0A';

        //  Bonus:
        //      if you will need to keep SigString size down, rather use PHP's gzdeflate or anything similar
        //      just don't forget if you later pull the compressed sig data from eg. database, to decompress it before sending it to SVG function
        //  $sigdecgz = gzdeflate($sigdec,9);

//      you can use these echos to see sample SigString
//      echo 'Printing decompressed SigString ; SetSigCompressionMode(0) :<br><br>';
//      echo $sigdec;

//      print the output of sigstring2svg, so my sample "AP" SigString, shown in SVG format, using slightly modified Scott's code
        echo '<br><br>Printing output of sigstring2svg() function, SigString as SVG (of decompressed & unencrypted signature !):<br><br>';
        echo sigstring2svg($sigdec);
        echo '<br><br>';

?>

OK, done with PHP ... but there is one more bonus for you folks!
I re-used same PHP code to actually draw the signature in HTML5 canvas, similar as the original SigWeb component does, but without using Topaz APIs/components.
I've re-used the PHP code to do the initial hex2bin and slicing
It is somewhat shorter this way, though not as polished as Scott's SVG.
Tested, works all nice even on combination of Ubuntu / Apache / PHP web server, with Android phone as client, so no Windows involved.

<script type="text/javascript">
    //  LuxZg - 08.05.2020.
    function cCanvas()
        {

            <?php

                //  sample signature, this SigString is hardcoded for example, you'd probably pull it from a database on server, hence PHP still makes sense for this step
                $sg = $sigdec;

                //  short version of PHP code

                //  convert hex to bin
                $raw = hex2bin($sg);
                //  cleanup double new-line after hex2bin conversion (\r\n)
                $raw = str_ireplace(array("\r",'\r'),'', $raw);
                //  exploding cleaned string to array
                $arr = explode(PHP_EOL, $raw);
                //  slicing coords to it's array
                $coords = array_slice($arr, 2, $arr[0]);
                    //  doing json encode to convert PHP array to JS array
                    $js_array_a = json_encode($coords);
                    //  echoing it as JS
                    echo "var coords = ". $js_array_a . ";\n";
                //  slicing line endings to it's array
                $lines = array_slice($arr, ($arr[0] + 2), $arr[1]); //Separate off number of coordinates pairs per stroke
                    //  and convert and echo to JSON as JS array for this as well
                    $js_array_b = json_encode($lines);
                    echo "var lines = ". $js_array_b . ";\n";

                //  server side done

            ?>

            //  now short client side JavaScript code

            //  define canvas that has HTML id = "c"; use any id just don't forget to change where needed
            var canvas = document.getElementById("c");
            //  and canvas' context
            var context = canvas.getContext("2d");
            //  get the coords array length
            var arrayLength = coords.length;
            //  initialize variables used in for loop
            var coordx = '';
            var coordy = '';
            var tocoords = [];

            //  putting coordinates/lines on canvas

            //  for all coordinates in the coords array
            for (var i = 0; i < arrayLength; i++) {
                    //  we split each coordinate to X & Y, can be shortened, this is for readability
                    tocoords = coords[i].split(" ");
                    coordx = tocoords[0];
                    coordy = tocoords[1];

                    //  if we encounter coord that is mentioned in lines[] array
                    //  it means line END, so we make a MOVE instead of drawing line, using moveTo() that coordinate
                    if (lines.includes(String(i)))
                        {
                            context.moveTo(coordx, coordy);
                        }
                    //  otherwise, we DRAW the line between points on canvas, using lineTo() that coordinate
                    else
                        {
                            context.lineTo(coordx, coordy);
                        }
                }

            //  at the end we have to define the color of the signature in the canvas
            context.strokeStyle = "#000000";    //  black
            //  and the thickness of the line, as you feel appropriate
            context.lineWidth = "3";
            //  and finally tell browser to make our lines into stroke, which is effectively command that shows signature in the canvas
            context.stroke();

            //  this ends client-side code, and we need just some basic HTML markup to execute it
        }

</script>

<br>
<div style="color:red; font-size:25px;" onclick="javascript:cCanvas()">Click to show signature</div>
<br>
Canvas is below, press button to show signature:
<br>
<canvas name="c" id="c" height="600" width="1500" ></canvas>
<br>

<!--
Canvas is fixed size, which is the unpolished part.
Edit - proposed solution:
I found this post to crop canvas, works well - https://stackoverflow.com/a/22267731/13312932
So you could start with empty no-size HTML5 canvas like so:
    <canvas id="c" />
Copy and add the function from that other answer and place it before my own *function cCanvas()*
 (you can ommit last 3 lines for image generation/tab opening)
And after defining *var canvas* temporarily expand it to really big canvas like so:
    // resize canvas temporarily
    canvas.width = 2000;
    canvas.height = 2000;
And then just after *context.stroke();* call the crop function like so:
    cropImageFromCanvas(context);
That's it, no surplus whitespace around signature, no guessing sizes, etc.
I will push new version to my GitHub later today/tomorrow for complete code.
-->
于 2020-05-09T09:21:25.707 回答
0

SigWeb SDK 提供了一种以图像格式获取签名字符串的方法。使用该图像,可以生成目标 Base64 字符串。这是一个基于 vb.net 的实现。

 Dim sigImage As System.Drawing.Image
 Dim sigObj As Topaz.SigPlusNET
 sigObj = New Topaz.SigPlusNET
 sigObj.SetSigCompressionMode(1)

 sigObj.SetSigString(sigWebSignatureString)
 sigObj.SetImageFileFormat(2)
 sigObj.SetImageXSize(100)
 sigObj.SetImageYSize(80)
 sigObj.SetImagePenWidth(1)
 sigObj.SetJustifyMode(5)
 sigImage = sigObj.GetSigImage()
 Using ms As New MemoryStream()
 System.Drawing.Image.Save(ms, Format)
      Dim imageBytes As Byte() = ms.ToArray()
      Dim base64String As String = Convert.ToBase64String(imageBytes)
 End Using
于 2020-01-17T08:52:35.510 回答