4Q-s-r-o / signature

Flutter plugin that creates a canvas for writing down a signature
MIT License
255 stars 85 forks source link

Feature Request: exportSVG() #9

Closed sam-androiddev closed 2 years ago

sam-androiddev commented 5 years ago

Please add support to export signature data as SVG.

MartinHlavna commented 4 years ago

I have looked up into this and it looks like neither Canvas nor PictureRecorder have ability to export data as SVG. Feel free to open PR if you have other solutions.

h7x4 commented 2 years ago

There are some quite advanced algorithms to do regression on a list of points and turning it into a proper bezier curve. But if you only need the SVG format itself, I think exporting the points to a list of <polyline> would be a possibility.

Example file (this might become quite big):

<svg viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg">
  <polyline points="116.00,85.45 121.45,89.45 130.18,96.00 140.73,104.73 152.73,113.82 165.09,124.73 181.09,136.73 198.18,152.00 218.91,170.55 238.91,188.36" fill="none" stroke="black" />
  <polyline points="98.91,131.64 101.82,130.91 106.55,130.55 112.73,130.18 120.00,129.45 128.73,129.09 137.82,128.00 146.91,127.27 155.27,126.55 163.64,125.82 171.27,125.09 179.64,124.73 188.36,124.00 197.09,123.64 205.09,123.64 213.82,123.64 221.09,123.64 230.18,123.64 238.18,123.64 244.73,123.64 250.91,123.64 256.36,126.18 260.36,129.09" fill="none" stroke="black" />
  <polyline points="254.91,78.91 254.91,81.82 254.91,86.55 254.18,93.82 253.82,102.18 253.45,110.18 253.45,117.82 253.45,125.09 253.45,132.73 253.45,140.36 253.45,147.64 253.45,155.27 253.45,162.18 253.45,168.73 253.45,174.91 252.36,180.73 252.36,186.55 252.36,190.91 252.36,195.27 252.00,199.27 252.00,203.64 251.27,208.73 251.27,212.73 251.27,216.73" fill="none" stroke="black" />
</svg>

Output:

MartinHlavna commented 2 years ago

@h7x4ABk3g Thanks for the idea!

We will evaluate this soon.

If I understand correctly algorithm would be something like:

  1. <svg viewBox="0 0 300 300" is the size of signature canvas. If user did not specify dimension we maybe could get dimensions by RenderBox
  2. We then need to convert points to a series of polylines
  3. Every two events of PointType.tapand all PointType.move points in between should form one polyline
h7x4 commented 2 years ago

I think the code I used to generate the image looked something like this:

const width = 300;
const height = 300;

List<List<Point>> strokes = [];
bool firstTap = true;
for (Point p in controller.points) {
  if (p.type == PointType.tap) {
    if (firstTap) {
      strokes.add([p]);
    } else {
      strokes.last.add(p);
    }
    firstTap = !firstTap;
  } else {
    strokes.last.add(p);
  }
}

String colorToHex(Color c) =>
    '#${c.value.toRadixString(16).padLeft(8, '0')}';
String formatPoint(Point p) =>
    '${p.offset.dx.toStringAsFixed(2)},${p.offset.dy.toStringAsFixed(2)}';

log(
  '<svg viewBox="0 0 $width $height" xmlns="http://www.w3.org/2000/svg">\n' +
      [
        for (final stroke in strokes)
          '<polyline '
              'fill="none" '
              'stroke="${colorToHex(controller.penColor)}" '
              'points="${stroke.map(formatPoint).join(' ')}" '
              '/>'
      ].join('\n') +
      '\n</svg>',
);

There's probably more attributes that should be added, like stroke width and background color.

Regarding the strokes, I believe that the undo/redo functionality already keeps track of this? Maybe the list of strokes should be exposed to the user as a property of the controller, or at least reused for this purpose? It feels like it would make the point.type property less useful though.

MartinHlavna commented 2 years ago

Maybe the list of strokes should be exposed to the user as a property of the controller, or at least reused for this purpose?

Yes, it could be reused indeed,

MartinHlavna commented 2 years ago

Hello all, just to update everyone we still don't have free time slot for this. If there is anyone willing to implement this and submit PR we will gladly accept.

MartinHlavna commented 2 years ago

Published in 5.2.0