我正在开发一个移动应用程序(ionic),其中有一个用于用户签名的字段。用户需要在画布上用手指签名。
现在这个应用程序有网络版本,他们使用黄玉sigweb来做到这一点。现在要保存签名并在两端查看,我需要某种转换。
我检查了sigweb的文档,他们以ASCHII十六进制格式保存签名。我不确定他们是否使用任何类型的内部加密,因为我无法将十六进制字符串转换为任何有效的 base 64。
我不确定是否有任何其他方法可以做到这一点。如果有人有任何想法,请分享。
提前谢谢。
我意识到这有点旧了,但我相信还有很多其他人有同样的问题。
当我需要在标准桌面或移动浏览器中向最终用户显示签名而无需插件或安装时,我遇到了完全相同的问题。与 Topaz 交谈,有一个选项可以使用 PHP 组件对象模型在服务器端运行 ActiveX 来创建映像。不幸的是,这仅适用于Windows服务器,并且需要大量摆弄。我只设法让它在我的测试台上运行,它对我的 Linux 生产服务器毫无用处。我再次联系了Topaz,他们说唯一的其他选择是Java小程序!?
ActiveX!爪哇小程序!现在是2019年,不是2009年!(对不起,我在那里用了过量的感叹号,但哎呀)。
最后我决定尝试自己解码,最终想出了这个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";
}
}
我花了 2 天时间摆弄这段代码,在过去的 10 年里,我花了无数个小时(甚至几个月)使用 Topaz 的 SigPlus/SigWeb。
当我在这里偶然发现斯科特的答案时,我只需要让它发挥作用。不幸的是,@ScottG犯了几个关键错误,所以它没有开箱即用,我的同事也因为缺乏文档而失败了。所以我的答案是 99% 建立在 Scott 的代码和输入之上,但我补充的是:
- 要使用的 SigString 示例
- 需要完成的修复
- 要求
- 更多评论
- Sig字符串结构说明
- HTML5 画布的奖励代码
无论如何,由于需要讲述整个故事,所以我将整个示例放在这里,而不仅仅是(固定的)PHP 函数。
如果您急于复制/粘贴代码并继续前进 - 请跳到实际代码。如果您想了解它是如何工作的,请阅读所有内容。
首先,我们需要知道这段代码(就像构建它的 Scott 一样)只有在 SigString 既没有压缩也没有加密的情况下才有效。如果您有以前的签名数据库,那么是时候将它们转换为未压缩/未加密的签名了。这是第一步,也是其他人立即放弃 Scott 代码的原因。不幸的是,解释压缩/解压缩超出了这个答案的范围(我达到了字符限制,所以请参阅我的 GitHub)。
以下是使用 Topaz 平板电脑生成的 SigString 示例,使用 SetSigCompressionMode(1) 保存("1" = 4:1 比率的无损压缩):
04002900C301E101FFFF00FF00FE00FD01FC00FA01FA02F802F803F704F604F604F604F503F502F402F402F301F300F301F200F300F201F100F200F100F100F202F202F203F303F305F404F504F604F703FA01FD01FF000034000902600000FF01FF01FF01FE010001000101000201030103010502070207030803080408040A060A060B070E080D070D060E060E050E050E050E040F030E030D020E020E010E010D010D010C010B010B010A010A000800070006FF0400040001FF01FF000000000000001B005A02F700FF00FE02FE02FD03FC02FC02FA02F900F801F6FFF500F5FEF5FFF5FEF7FEF8FEF9FEFBFEFCFEFDFEFEFE00FF00FF0000010000004D000803A1010000FF010000FF00FFFEFFFEFEFCFFFCFEFCFEFAFDFAFEF8FDF8FCF7FCF6FBF6FCF6FBF5FCF5FCF5FBF5FDF5FCF5FDF4FCF4FDF5FDF4FDF4FEF5FEF5FFF500F600F501F701F702F804F804F904FA06FB08FB09FD0BFD0DFF0C010D020E020C040E040C050B0509060807060805080308020801080009FF08FE08FD08FC07FB06FB06F805F703F402F301F100F1FEF2FEF6FEFBFEFE000000
这是转换为 SetSigCompressionMode(0) 的相同 SigString(无压缩):
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"]
我们通过创建另一个包含"行"值的数组来完成切片(同样,只要原始数组的第二个元素说,这个数组的长度就会被削减):
["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 canvas"代码中看到的那样,您实际上并没有像在 SVG 中那样定义行开始/结束,您只需移动铅笔,但有时它会"在纸上"有时"在空中"移动
- 等
如前所述,您只需要确保使用所有坐标,并更正原始代码,这是最简单的方法(单行),无需更多重写。
知道我们现在拥有什么,Scott选择了一个包含另一个切片代码的解决方案。他正在将我们的 coord[] 数组切成单独的线,嗯,折线,他稍后输出到 SVG。由于 SVG 的格式,这实际上是首选方式,或者至少在查看 Scott 如何做到这一点以及 SVG 作为最终产品在 HTML 中的实际外观时,我认为它是这样。
但不幸的是,原始代码有 2 个错误(我花了 2 天时间才解决:))。 一个错误是缺少最后一行,根据您的示例签名,它可能并不明显。我的第一个样本只有一个点(签名带有"i"字母,所以无论谁签名,都会在最后的"i"上画点,所以它根本不引起注意,只是少了一个点)。但是我在这里提供的这个"AP"示例实际上将整个字母"P"作为最后一行,因此缺少整个字母使其非常明显。无论如何,我解释了我是如何解决这个问题的,所以继续第二个问题。
另一个错误是切片到段/折线时切片长度使用了错误的值。它实际上使用最后一个点来切片它,但array_slice PHP函数期望(数组,起点,长度),而Scott可能期望它是(数组,起点,终点)。这在一些签名样本中也很微妙。本质上发生的事情是一些点被重复使用,但如果你在一行上面写一条线,那就没关系了(特别是在完美的数字绘图中)。但!通过重复使用坐标,它有时会在两条或多条线组合的路径上延伸一条线,基本上否定了"笔"在空中"时会发生什么,突然间所有的签名看起来都好像笔从未从纸上移开。例如。"i"上的"点"将是连接"i"和"点"的一条线(或者更糟糕的是,即使你写了"线"并在最后一个字母后点缀了"i",它也会将点与"e"连接起来,现在它弹出来了)
我修复了这个问题,所以现在它切片如下(再次基于我的示例 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 的版本相同,进行比较。
我在我添加的行上发表了评论,如果这是斯科特行的更改,我会将他的行放在我的固定行上方,以便您轻松比较。 我没有标记我和他的所有评论,因为这些无论如何都可以跳过,我的评论只是补充解释或解释我的更改。
这里也是一个指向我的 GitHub 存储库的链接,因此您也可以在那里看到整个代码以及完整的文章,因为由于堆栈溢出字符限制,我不得不在这里缩短内容: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 rn 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 rn
// echo '<pre>'.json_encode($raw).'</pre>'; // this did show rn , 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 (rn)
$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.
-->
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