Products Technologies Demo Docs Blog Support Company

Merging Signature Annotations into Documents

The latest version of the TX Text Control Document Viewer can be used to sign any document with an annotation signature. This sample shows how to merge those SVG images into documents server-side.

Merging Signature Annotations into Documents

Using the TX Text Control Document Viewer, electronic signatures can be requested from users to sign a pre-defined signature field. This signature field is then replaced with the acquired signature image and digitally signed using a digital certificate.

Instead of signing pre-defined fields, the latest version of the Document Viewer is able to acquire a signature image from users in order to insert the signature image as an annotation object to the document. Those annotation objects are stored independently from the document and can be retrieved using the annotations.export JavaScript method.

Signature Annotations

Signature annotations can be added using a new button in the annotation toolbar:

Signing Documents

Using the signature soft pad, multiple signatures can be drawn or uploaded. Those signatures are stored locally and can be used across sessions:

Signing Documents

After placing a signature onto a document, they are part of the annotation collection.

Signing Documents

Exporting Annotations

When exporting the annotations as JSON using the annotations.export method, the placed signature is added as an SVG image. In this sample application, this JSON object is being sent to the HttpPost Controller method MergeAnnotations:

function storeAnnotations() {

  var annotations = TXDocumentViewer.annotations.export();

  var obj = JSON.parse(annotations);
  var serviceURL = '@Url.Action("MergeAnnotations", "Home")';

  $.ajax({
    type: 'POST',
    url: serviceURL,
    contentType: 'application/json',
    dataType: 'json',
    data: JSON.stringify(obj),
    success: successFunc,
    error: errorFunc
  });

  function successFunc(data, status) {
    TXDocumentViewer.loadDocument(data.data, 'results.tx');
  }
  
  function errorFunc() {
    alert('error');
  }

}

Inserting Annotations as Images

Server-side, the array of Annotation arrays is received as Annotation objects defined through the following model:

public class Location {
  public double x { get; set; }
  public double y { get; set; }
}

public class Pen {
  public string type { get; set; }
  public double objectWidth { get; set; }
  public double objectHeight { get; set; }
}

public class Annotation {
  public Pen pen { get; set; }
  public string user { get; set; }
  public Location location { get; set; }
  public long time { get; set; }
  public List<Comment> comments { get; set; }
  public string id { get; set; }
  public string image { get; set; }
}

public class Comment {
  public string comment { get; set; }
  public List<string> user { get; set; }
  public long date { get; set; }
  public string id { get; set; }
  public string status { get; set; }
}

In the controller method, the SVG signature image representation of each annotation is used to create a new Image object from a MemoryStream. Finally, the image is added to the document at the given location and page number.

[HttpPost]
public string MergeAnnotations() {

   Stream inputStream = Request.InputStream;
   inputStream.Position = 0;

   StreamReader str = new StreamReader(inputStream);
   string sBuf = str.ReadToEndAsync().Result;

   List<List<TXTextControl.SignatureAnnotation.Annotation>> annotations =
      JsonConvert.DeserializeObject<List<List<TXTextControl.SignatureAnnotation.Annotation>>>(sBuf);

   byte[] bTx;
   int iPageNumber = 0;

   string prePng = "data:image/png;base64,";
   string preSvg = "data:image/svg+xml;utf8,";

   // calculate the current resolution
   var dpi = 1440 / DocumentController.DpiX;

   // create temporary ServerTextControl
   using (ServerTextControl tx = new ServerTextControl()) {
      tx.Create();

      // load the document
      tx.Load(Server.MapPath("~/App_Data/Documents/template_sign.tx"), StreamType.InternalUnicodeFormat);

      // loop through the array of annotations
      foreach (List<TXTextControl.SignatureAnnotation.Annotation> pages in annotations) {

         iPageNumber++;

         foreach (TXTextControl.SignatureAnnotation.Annotation annotation in pages) {

            // handle only signatures
            if (annotation.pen.type != "signature")
               continue;

            byte[] bytes;

            // get SVG or PNG as bytes and remove the prefix
            if (annotation.image.StartsWith(prePng))
               bytes = Convert.FromBase64String(annotation.image.Remove(0, prePng.Length));
            else
               bytes = Encoding.UTF8.GetBytes(annotation.image.Remove(0, preSvg.Length));

            // create a memory stream from SVG
            using (MemoryStream ms = new MemoryStream(
               bytes, 0, bytes.Length, writable: false, publiclyVisible: true)) {

               // TX image from memory stream
               TXTextControl.Image img = new TXTextControl.Image(ms);

               // add the image as a fixed object on the current page (array)
               tx.Images.Add(
                  img,
                  iPageNumber,
                  new System.Drawing.Point(0, (int)(annotation.location.y * dpi)),
                  TXTextControl.ImageInsertionMode.BelowTheText | TXTextControl.ImageInsertionMode.FixedOnPage);

               // set the location
               img.Location = new System.Drawing.Point(
                     (int)(annotation.location.x * dpi),
                     img.Location.Y);

            }

         }

      }

      // save the document as PDF
      tx.Save(out bTx, TXTextControl.BinaryStreamType.InternalUnicodeFormat);
   }

   // return as base64 encoded string
   return Convert.ToBase64String(bTx);
}

After all images have been added, the document is saved and returned to the client. This way, those signature annotations can be also embedded into PDF documents.

Stay in the loop!

Subscribe to the newsletter to receive the latest updates.

Also See

This post references the following in the documentation:

  • Javascript: Annotations.export Method
  • TXTextControl.Image Class

GitHub

Download and Fork This Sample on GitHub

We proudly host our sample code on github.com/TextControl.

Please fork and contribute.

Download ZIP

Open on GitHub

Open in Visual Studio

Requirements for this sample

  • TX Text Control .NET Server 31.0
  • Visual Studio 2022

Related Posts

ASP.NETASP.NET CoreDocument Viewer

High-Performance Text Replacement in Large DOCX Files using C# .NET

Learn how to efficiently replace text in large DOCX files using C# .NET and the ServerTextControl component from Text Control. This article demonstrates the performance benefits of using the…


ASP.NETASP.NET CoreDocument Viewer

Document Viewer 33.2.1 Released: New Event and Bug Fixes

This service pack includes important bug fixes and improvements to enhance the stability and performance of the Document Viewer. In addition, a new event has been introduced to provide developers…


AngularASP.NETBlazor

Building an ASP.NET Core Backend (Linux and Windows) for the Document Editor…

This article shows how to create a backend for the Document Editor and Viewer using ASP.NET Core. The backend can be hosted on Windows and Linux and can be used in Blazor, Angular, JavaScript, and…


AngularASP.NETJavaScript

Using the Document Editor in SPA Applications using the removeFromDom Method

This article shows how to use the removeFromDom method to remove the Document Editor from the DOM when it is no longer needed. This is useful when the Document Editor is used in a Single Page…


ASP.NETASP.NET CoreDocument Viewer

Document Viewer: Long Polling Support for Loading Documents

The Document Viewer now supports long polling for loading documents. This allows an asynchronous loading of documents and is especially useful for large documents or slow connections.