How to Add Electronic Signatures to PDF Documents Using JavaScript and HTML5

An e-signature is a digital form of a handwritten signature that allows individuals to sign documents electronically. It has become an essential tool for businesses and organizations to streamline the signing process, enabling documents to be signed in just a few clicks from anywhere in the world. In this article, we will explore how to insert a signature into PDF documents using HTML5, JavaScript, and Dynamsoft Document Viewer.

What you’ll build: A web-based PDF editor that lets users draw freehand electronic signatures on an HTML5 Canvas and stamp them onto any page of a PDF document using JavaScript and the Dynamsoft Document Viewer SDK.

Key Takeaways

  • Dynamsoft Document Viewer’s annotationManager.createAnnotation API can insert a canvas-drawn signature as a stamp annotation on any PDF page.
  • The HTML5 Canvas toBlob() method converts a freehand drawing into a PNG image suitable for PDF embedding.
  • Signature position, stroke color, and line width are fully configurable; signatures can be batch-applied to all pages in a single loop.
  • This approach works in any modern browser without server-side processing or native plugins.

Common Developer Questions

  • How do I add a digital signature to a PDF using JavaScript and HTML5?
  • How can I let users draw a freehand signature and insert it into a PDF in the browser?
  • What JavaScript SDK supports PDF annotation with electronic signatures?

Demo Video: Sign PDF Documents with Electronic Signature

Try the Online Demo

https://yushulx.me/web-document-annotation/

Prerequisites

Build a PDF Signature Feature in a Web Editor

Our web document annotation project, built with the Dynamsoft Document Viewer, is highly extensible. It currently includes barcode detection for PDF documents. In the following sections, we will guide you through the process of adding an electronic signature to PDF documents step by step.

Step 1: Clone the Source Code Repository

  1. Clone the source code from the GitHub repository:

     git clone https://github.com/yushulx/web-twain-document-scan-management.git
    
  2. Navigate to the document_annotation directory:

     cd web-twain-document-scan-management/examples/document_annotation
    
  3. Open the project in Visual Studio Code.

Step 2: Add a Signature Button to the Toolbar

  1. In main.css, add a material icon for the signature button:

     .icon-stylus::before {
         content: "edit";
     }
        
     .icon-stylus {
         display: flex;
         font-size: 1.5em;
     }
    
    

    stylus button

  2. Define the signature button and add it to the toolbar in main.js:

     const signatureButton = {
         type: Dynamsoft.DDV.Elements.Button,
         className: "material-icons icon-stylus",
         tooltip: "Sign the document",
         events: {
             click: "sign",
         }
     }
    
     const pcEditViewerUiConfig = {
         type: Dynamsoft.DDV.Elements.Layout,
         flexDirection: "column",
         className: "ddv-edit-viewer-desktop",
         children: [
             {
                 type: Dynamsoft.DDV.Elements.Layout,
                 className: "ddv-edit-viewer-header-desktop",
                 children: [
                     {
                         type: Dynamsoft.DDV.Elements.Layout,
                         children: [
                             Dynamsoft.DDV.Elements.ThumbnailSwitch,
                             Dynamsoft.DDV.Elements.Zoom,
                             Dynamsoft.DDV.Elements.FitMode,
                             Dynamsoft.DDV.Elements.Crop,
                             Dynamsoft.DDV.Elements.Filter,
                             Dynamsoft.DDV.Elements.Undo,
                             Dynamsoft.DDV.Elements.Redo,
                             Dynamsoft.DDV.Elements.DeleteCurrent,
                             Dynamsoft.DDV.Elements.DeleteAll,
                             Dynamsoft.DDV.Elements.Pan,
                             Dynamsoft.DDV.Elements.AnnotationSet,
                             qrButton,
                             checkButton,
                             scanButton,
                             clearButton,
                             signatureButton,
                         ],
                     },
                     {
                         type: Dynamsoft.DDV.Elements.Layout,
                         children: [
                             {
                                 type: Dynamsoft.DDV.Elements.Pagination,
                                 className: "ddv-edit-viewer-pagination-desktop",
                             },
                             loadButton,
                             downloadButton,
                         ],
                     },
                 ],
             },
             Dynamsoft.DDV.Elements.MainView,
         ],
     };
    
  3. Add the click event handler for the signature button:

     editViewer.on("sign", sign);
    
     function sign() {
         ...
     }
    

Step 3: Create a Signature Input Dialog

The pop-up dialog for signature input includes the following elements:

  • A canvas element for drawing the signature.
  • Color options for changing the signature color.
  • A stroke slider to adjust signature thickness.
  • X and Y coordinates for positioning the signature.
  • A save button to insert the signature into the PDF document.
  • A cancel button to close the dialog.
  • A redraw button to erase the signature.

HTML Code

<div id="signature-input" class="overlay">
    <div class="signature-container">
        <h2>Create New Signature</h2>
        <canvas id="signatureCanvas"></canvas>
        <div class="form-group">
            <span>Color Style:</span>
            <div id="blue" class="color-button" style="background-color: blue;"></div>
            <div id="red" class="color-button" style="background-color: red;"></div>
            <div id="black" class="color-button" style="background-color: black;"></div>
        </div>
        <div class="form-group">
            <span>Stroke:</span>
            <input type="range" id="strokeSlider" min="1" max="10" value="3">
        </div>

        <!-- X -->
        <div class="form-group">
            <label for="signatureX">X:</label>
            <input type="text" id="signatureX" name="signatureX" placeholder="0">
        </div>

        <!-- Y -->
        <div class="form-group">
            <label for="signatureY">Y:</label>
            <input type="text" id="signatureY" name="signatureY" placeholder="0">
        </div>

        <div style="margin-top: 10px;">
            <input type="checkbox" id="signatureAllPage"> Auto-generate signatures on all pages
        </div>


        <div class="popup-buttons">
            <button id="signatureOK">OK</button>
            <button id="signatureRedraw">Redraw</button>
            <button id="signatureCancel">Cancel</button>
        </div>
    </div>
</div>

Signature input dialog

JavaScript code for the signature input dialog

  1. Initialize the canvas and drawing context:

     let canvas = document.getElementById("signatureCanvas");
     let ctx = canvas.getContext("2d");
     let isDrawing = false;
     let color = "black"; 
     let strokeWidth = 3;  
     let drawingHistory = []; 
     canvas.width = 500;
     canvas.height = 300;
    
  2. Add event listeners for mouse events:

     canvas.addEventListener("mousedown", startDrawing);
     canvas.addEventListener("mousemove", draw);
     canvas.addEventListener("mouseup", stopDrawing);
     canvas.addEventListener("mouseout", stopDrawing);
    
  3. Add event listeners for color and stroke options:

     document.getElementById("blue").addEventListener("click", () => {
         color = "blue";
         redrawCanvas();
     });
        
     document.getElementById("red").addEventListener("click", () => {
         color = "red";
         redrawCanvas();
     });
        
     document.getElementById("black").addEventListener("click", () => {
         color = "black";
         redrawCanvas();
     });
        
     document.getElementById("strokeSlider").addEventListener("input", (e) => {
         strokeWidth = e.target.value;
         redrawCanvas();
     });
    
  4. Implement the drawing functions:

     function startDrawing(event) {
         isDrawing = true;
         let currentPath = {
             color: color,
             strokeWidth: strokeWidth,
             points: [{ x: event.offsetX, y: event.offsetY }]
         };
         drawingHistory.push(currentPath);
     }
        
     function draw(event) {
         if (isDrawing) {
             let currentPath = drawingHistory[drawingHistory.length - 1];
             currentPath.points.push({ x: event.offsetX, y: event.offsetY });
             redrawCanvas();
         }
     }
        
     function stopDrawing() {
         isDrawing = false;
     }
        
     function redrawCanvas() {
         ctx.clearRect(0, 0, canvas.width, canvas.height);
        
         drawingHistory.forEach(path => {
             ctx.beginPath();
             ctx.moveTo(path.points[0].x, path.points[0].y);
             for (let i = 1; i < path.points.length; i++) {
                 ctx.lineTo(path.points[i].x, path.points[i].y);
             }
             ctx.strokeStyle = color;
             ctx.lineWidth = strokeWidth;
             ctx.stroke();
         });
     }
        
     function clearCanvas() {
         drawingHistory = [];
         redrawCanvas();
     }
    

    All paths are stored in the drawingHistory array. The redrawCanvas function iterates through the array and redraws the paths on the canvas.

  5. Add event listeners for the OK, Redraw, and Cancel buttons:

    
        
     const signatureOKButton = document.getElementById('signatureOK');
     const signatureRedrawButton = document.getElementById('signatureRedraw');
     const signatureCancelButton = document.getElementById('signatureCancel');
    
     signatureOKButton.addEventListener('click', async () => {
         ...
     });
    
     signatureRedrawButton.addEventListener('click', async () => {
         drawingHistory = [];
         redrawCanvas();
     });
    
     signatureCancelButton.addEventListener('click', async () => {
         document.getElementById("signature-input").style.display = "none";
     });
    

Step 4: Insert the Signature into PDF Pages

To insert the signature into the PDF document:

  1. Convert the canvas to a blob:

     canvas.toBlob(async (blob) => {
         ...
     }, 'image/png');
    
  2. Get the current page ID and page data:

     let currentPageId = currentDoc.pages[editViewer.getCurrentPageIndex()];
     let pageData = await currentDoc.getPageData(currentPageId);
    
  3. Create new stamp annotations with the signature image:

        
     const applyToAllPages = document.getElementById("signatureAllPage").checked;
    
     const x = Number(document.getElementById("signatureX").value);
     const y = Number(document.getElementById("signatureY").value);
    
     const option = {
         stamp: blob,
         x: x > pageData.mediaBox.width - canvas.width ? pageData.mediaBox.width - canvas.width - 10 : x,
         y: y > pageData.mediaBox.height - canvas.height ? pageData.mediaBox.height - canvas.height - 10 : y,
         width: canvas.width,
         height: canvas.height,
         opacity: 1.0,
         flags: {
             print: false,
             noView: false,
             readOnly: false,
         }
     }
    
     try {
         if (applyToAllPages) {
             for (let i = 0; i < currentDoc.pages.length; i++) {
                 let signatureAnnotation = await Dynamsoft.DDV.annotationManager.createAnnotation(currentDoc.pages[i], "stamp", option)
                 signatureAnnotation['name'] = 'signature';
             }
         } else {
             let signatureAnnotation = await Dynamsoft.DDV.annotationManager.createAnnotation(currentPageId, "stamp", option)
             signatureAnnotation['name'] = 'signature';
         }
     } catch (e) {
         console.log(e);
     }
    

    PDF document with electronic signature

Common Issues and Edge Cases

  • Signature appears outside the visible page area: If the X/Y coordinates exceed the page dimensions, the stamp is placed off-screen. The code clamps the position to pageData.mediaBox.width - canvas.width and pageData.mediaBox.height - canvas.height, but always validate user input before calling createAnnotation.
  • Canvas is blank when clicking OK: This happens when a user clicks OK without drawing anything. Check that drawingHistory.length > 0 before converting the canvas to a blob to avoid inserting an empty stamp.
  • Signature renders at unexpected size on high-DPI screens: The canvas width/height attributes are fixed at 500×300 CSS pixels. On Retina displays, consider scaling the canvas backing store by window.devicePixelRatio so the exported PNG matches the on-screen resolution.

Source Code

https://github.com/yushulx/web-twain-document-scan-management/tree/main/examples/document_annotation