Skip to Content

Hi all,

Recently I have got a requirement in which I need to display the captured digital signature in editable mode.

SVG is is a graphic container element in HTML which stands for Scalable Vector Graphics. It used for defining graphics for the web.

Earlier we used to capture and display signatures using Canvas. But I found it is better opting for SVG rather than going for Canvas for my requirement after going through many links. I also came know that when ever we need to make a change in Canvas we need to redraw the entire scene. The main advantage of SVG is it is scalable and it supports event handlers.

Firstly I have created an OData service to get and create an entity.

The logic for get entity operation: 

DATA: lt_keys       TYPE /iwbep/t_mgw_tech_pairs,
      ls_key        TYPE /iwbep/s_mgw_tech_pair,
      LV_name TYPE char20.

   lt_keys = io_tech_request_context->get_keys( ). //Get key elements
**Reading key properties
    READ TABLE lt_keys WITH KEY  NAME = 'NAME'
                                INTO ls_key.
***Setting key value to a local variable
   lv_name = ls_key-value.
***Retrieving data
  SELECT SINGLE * from ztsvg_signature into ER_ENTITY WHERE Name = lv_name.

Create operation:

Logic is as follows:

DATA: wsvg_signature TYPE ztsvg_signature.
**Read request data
IO_DATA_PROVIDER->READ_ENTRY_DATA( IMPORTING ES_DATA = wsvg_signature ).

ER_ENTITY-Name   = wsvg_signature-Name.
ER_ENTITY-SIGNURL  = wsvg_signature-SIGNURL.

INSERT ztsvg_signature FROM ER_ENTITY.

In order to create an entity, perform get to read a single entity and click on use as request to perform post operation.

Create the entity as highlighted in the HTTP Request and select HTTP method as Post. Click on execute

Custom Controller: 

In the custom controller, we are providing meta data to create a signature pad, its properties to capture mouse movement and the functions to get, set and clear signature details. The logic is as follows.

jQuery.sap.declare("com.finalcanvasFinal.controller.SignaturePad");
sap.ui.core.Control.extend("com.finalcanvasFinal.controller.SignaturePad", {	
  metadata: {
      properties: {
        width: {type: 'int', defaultValue: 300},
        height: {type: 'int', defaultValue: 100},
        bgcolor: {type: 'string', defaultValue: '#ffa'},
        lineColor: {type: 'string', defaultValue: '#666'},
        penColor: {type: 'string', defaultValue: '#333'},
        signature: 'string'
      }
    },
    
    renderer: function(oRm, oControl) {
      var bgColor = oControl.getBgcolor();
      var lineColor = oControl.getLineColor();
      var pen = oControl.getPenColor();
      var id = oControl.getId();
      var w = oControl.getWidth();
      var h = oControl.getHeight();
      oRm.write("<div");
      oRm.writeControlData(oControl);
      oRm.write(">");
      oRm.write('<svg xmlns="http://www.w3.org/2000/svg" width="' + w +
                '" height="' + h + '" viewBox="0 0 ' + w + ' ' + h + '">');
      
      oRm.write('<rect id="' + id  + '_r" width="' + w + '" height="' + h + 
                '" fill="' + bgColor  + '"/>');
      
      var hh = h - 20;
      oRm.write('<line x1="0" y1="' + hh  + '" x2="' + w + '" y2="' + hh + 
                '" stroke="' + lineColor + 
                '" stroke-width="1" stroke-dasharray="3" ' + 
                'shape-rendering="crispEdges" pointer-events="none"/>');
      
      oRm.write('<path id="' + id + '_p" stroke="' + pen + '" stroke-width="2" ' +
                'fill="none" pointer-events="none"/>');
      oRm.write('</svg>');
      oRm.write("</div>");
    },
    
    clear: function() {
    	var that = this;
      that.signaturePath = '';
      var p = document.getElementById(that.getId() + '_p');
      p.setAttribute('d', '');
    },

    onAfterRendering: function() {
      var that = this;
      that.signaturePath ='';
      isDown = false;
      var elm = that.$()[0];
      var r = document.getElementById(that.getId() + '_r');
      var p = document.getElementById(that.getId() + '_p');

      function isTouchEvent(e) {
        return e.type.match(/^touch/);
      }

      function getCoords(e) {
        if (isTouchEvent(e)) {
          return e.targetTouches[0].clientX + ',' +
            e.targetTouches[0].clientY;
        }
        return e.clientX + ',' + e.clientY;
      }

      function down(e) {
        that.signaturePath += 'M' + getCoords(e) + ' ';
        p.setAttribute('d', that.signaturePath);
        isDown = true;
        if (isTouchEvent(e)) e.preventDefault();
      }

      function move(e) {
        if (isDown) {
          that.signaturePath += 'L' + getCoords(e) + ' ';
          p.setAttribute('d', that.signaturePath);
        }
        if (isTouchEvent(e)) e.preventDefault();
      }

      function up(e) {
        isDown = false; 
        if (isTouchEvent(e)) e.preventDefault();
      }

      r.addEventListener('mousedown', down, false);
      r.addEventListener('mousemove', move, false);
      r.addEventListener('mouseup', up, false);
      r.addEventListener('touchstart', down, false);
      r.addEventListener('touchmove', move, false);
      r.addEventListener('touchend', up, false);
      r.addEventListener('mouseout', up, false);
     //To get signature path 
       if (this.getSignature()) {
        this.signaturePath = this.getSignature();
        var p = document.getElementById(this.getId() + '_p');
        if (p) {
          p.setAttribute('d', this.signaturePath);
        }
      }
      // to set signature path to the signaturepad to display
       that.setSignature = function(s) {
        that.setProperty('signature', s);
        that.invalidate();
      };
     }

  });

Controller Logic:

Here I have accessed the custom controller to load all the metadata to create SVG Container and all the functions necessary to draw SVG graphics.

I have created an instance for signature pad and all other UI elements like buttons and input field for name,.

//Global variables
var prevSignature;
var isDown;
sap.ui.define([
	"sap/ui/core/mvc/Controller",
	"com/finalcanvasFinal/controller/SignaturePad"
], function(Controller, SignaturePad) {
	"use strict";

	return Controller.extend("com.finalcanvasFinal.controller.View1", {
		onInit: function() {
		//instance for signature pad	
			var oCtrl = new SignaturePad({
				id: "signPad",
				width: 400,
				height: 200
			});
		//Adding signature pad to page content
			var content = this.byId("signEdit");
			content.addContent(oCtrl);
			var that = this;
		//Input field for Name	
			var textField = new sap.m.Input({
				id: "nameValue",
				width: '10rem',
				placeholder: 'Name'

			});
			content.addContent(textField);
		//Clear button	to clear signaturePad
			var clearButton = (new sap.m.Button({
				text: 'Clear',
				press: function() {
					oCtrl.getSignature();
					sap.ui.getCore().byId("nameValue").setValue(" ");
					oCtrl.clear();
				}
			}));
			content.addContent(clearButton);
		//Accept button to save data in backend
			var acceptButton = (new sap.m.Button({
				text: 'Save',
				press: function() {
					
					var signaturePath = oCtrl.getSignature(); 
					if (signaturePath !== ' ') {

						var oEntry = {}; // instantiating the object
						oEntry.Name = sap.ui.getCore().byId("nameValue").getValue();	//Name
						oEntry.Signurl = signaturePath;									//SVG path
						var sServiceUrl = "/sap/opu/odata/sap/ZSVG_SIGNATURES_SRV/";
						var oModel = new sap.ui.model.odata.ODataModel(sServiceUrl, false);
						
						oModel.create("/SigntureSet()", oEntry, null, function(response) {
							sap.m.MessageBox.show("Success! Data is saved");
						}, function(error) {
							sap.m.MessageBox.show("Error!");

						});
					}
				}

			}));
			content.addContent(acceptButton);
			// Adding display button
			var Display = new sap.m.Button({
				text: 'Display',
				enabled: true,
				// Displaying signature based on input		
				press: function() {
					var Name = sap.ui.getCore().byId("nameValue").getValue(); //getting input value

					var sServiceUrl = "/sap/opu/odata/sap/ZSVG_SIGNATURES_SRV/";
					var oModel = new sap.ui.model.odata.ODataModel(sServiceUrl, false);
					//reading details based on name
					oModel.read("/SigntureSet(Name='" + Name + "')", null, null, false, function(oData, oResponse) {
						prevSignature = oData.Signurl;
						//Calling set signatuer method from custom controller
						oCtrl.setSignature(prevSignature);
					});
				}
			});
			content.addContent(Display);
		}

	});
});

My view looks as follows:

When a name and signature is provided and hit save, the name and SVG path will get saved to the backend system.

In order to display the signature provide the name and click on display. The result is as follows.

Thank you..!! 🙂 🙂

 

 

To report this post you need to login first.

6 Comments

You must be Logged on to comment or reply to a post.

  1. Prabhakaran Palanisamy

    That’s one interesting topic! Thanks for sharing.

    Is this signature captured through FIRST PHONES/ Pin Pad?  (or)

    What is the phone or touch device through which this signature is captured?

     

    Kind Regards,

    Prabha

    (1) 
    1. Sri Divya Post author

      Hi Prabhakaran,

       

      I had tested it on Desktop.I will test it on phone/ tab and will let you know.

       

      Thank you,

      Sri Divya.

       

      (0) 
  2. jagadeesh y

    Great Work

    SVG Canvas
    Vector based (composed of shapes) Raster based (composed of pixel)
    Multiple graphical elements, which become the part of the DOM Single HTML element similar to <img> in behavior
    Modified through script and CSS Modified through script only
    Give better performance with smaller number of objects or larger surface, or both Give better performance with smaller surface or larger number of objects, or both
    Better scalability — can be printed with high quality at any resolution Poor scalability — not suitable for printing on higher resolution

    Thank You Sri Divya Bandaru 

    (1) 
      1. jagadeesh y

        Hi divya,

        I’m involved with a project where I need to use a routing concept

        if i declare same pattern for two different views (view1,view2 same pattern) if i call the pattern using routing API what will happen

        is it call 1st view or 2nd view or we will get error?

        Thanks

         

        (1) 
        1. Sri Divya Post author

          Hi Jagdeesh,

           

          It won’t raises any error even if we use same pattern for 2 views.

          Based on the route you hit the corresponding target view will get loaded.

          In case of single route the first view (out of two views with same pattern) whose pattern matches will get loaded.

          Thank you,

          SriDivya.

          (0) 

Leave a Reply