Sign PDF in order

This sample presents a PDF ('contract') containing multiple signature locations where two users ('buyer' and 'seller') need to sign the document. Code in config.js ensures that the users are guided through all signature locations in order when they sign the document.

window.onload = function() { const viewer = new DsPdfViewer("#viewer", getViewerOptions()); configureViewerUI(viewer); viewer.open("/document-solutions/javascript-pdf-viewer/demos/product-bundles/assets/pdf/sign-pdf-in-order.pdf"); }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Sign PDF in order</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="./src/styles.css"> <script src="/document-solutions/javascript-pdf-viewer/demos/product-bundles/build/dspdfviewer.js"></script> <script src="/document-solutions/javascript-pdf-viewer/demos/resource/js/init.js"></script> <script src="./src/app.js"></script> <script src="./src/config.js"></script> </head> <body> <div id="viewer"></div> </body> </html>
#viewer { height: 100%; }
// exported global methods: var getViewerOptions, configureViewerUI; (function () { // Graphical signature locations. // Note that the { x, y } origin is at the bottom left. var graphicalSignatureLocationsBottomLeft = { // The key is the name of the PDF file in lower case without extension. "sign-pdf-in-order": [ { pageIndex: 0, x: 187, y: 73, w: 80, h: 20, title: "Buyer" }, { pageIndex: 0, x: 420, y: 73, w: 80, h: 20, title: "Seller", color: "#1234dd" }, { pageIndex: 1, x: 187, y: 73, w: 80, h: 20, title: "Buyer" }, { pageIndex: 1, x: 420, y: 73, w: 80, h: 20, title: "Seller", color: "#1234dd" }, { pageIndex: 2, x: 187, y: 73, w: 80, h: 20, title: "Buyer" }, { pageIndex: 2, x: 420, y: 73, w: 80, h: 20, title: "Seller", color: "#1234dd" }, { pageIndex: 3, x: 187, y: 73, w: 80, h: 20, title: "Buyer" }, { pageIndex: 3, x: 420, y: 73, w: 80, h: 20, title: "Seller", color: "#1234dd" }, { pageIndex: 4, x: 187, y: 73, w: 80, h: 20, title: "Buyer" }, { pageIndex: 4, x: 420, y: 73, w: 80, h: 20, title: "Seller", color: "#1234dd" }, { pageIndex: 5, x: 187, y: 73, w: 80, h: 20, title: "Buyer" }, { pageIndex: 5, x: 420, y: 73, w: 80, h: 20, title: "Seller", color: "#1234dd" } ] }; // Parameters for the "sign date" labels const signDateLabelParams = { // x offset from the position of the graphical signature offset_x: 101, // y offset from the position of the graphical signature offset_y: 0, // label width w: 58, // label height h: 14 } getViewerOptions = function getViewerOptions() { return { supportApi: { apiUrl: window.top.SUPPORTAPI_URL, // e.g. "http://localhost:5004/api/pdf-viewer"; token: window.top.SUPPORTAPI_TOKEN, // e.g. "support-api-demo-net-core-token"; webSocketUrl: false }, restoreViewStateOnLoad: false }; } configureViewerUI = function (viewer) { viewer.addDefaultPanels(); viewer.addAnnotationEditorPanel(); viewer.onAfterOpen.register(function () { populateSignature(viewer); }); } async function populateSignature(viewer) { viewer.__onSignatureLinkEvent = onSignatureLinkEvent; const locationsBottomLeft = graphicalSignatureLocationsBottomLeft[viewer.fileName.toLowerCase().replace(".pdf", "")]; if (locationsBottomLeft) { for (let i = 0; i < locationsBottomLeft.length; i++) { const locationInfo = locationsBottomLeft[i]; const pageIndex = parseInt(locationInfo.pageIndex); const signTitle = locationInfo.title; const rect = [locationInfo.x, locationInfo.y, locationInfo.x + locationInfo.w, locationInfo.y + locationInfo.h]; const signUiId = "signui_" + i; const freeTextLabel = { annotationType: 3, // AnnotationTypeCode.FREETEXT subject: signUiId, borderStyle: { width: 1, style: 2 }, fontSize: 6, appearanceColor: "#fff59d", color: locationInfo.color || "#f44336", contents: "Sign Here", textAlignment: 1, rect: [rect[0], rect[3] - 12, rect[0] + 35, rect[3]] }; await viewer.addAnnotation(pageIndex, freeTextLabel, { skipPageRefresh: true }); const viewerSelector = "#" + viewer.hostElement.id; const linkAnnotation = { annotationType: 2, // AnnotationTypeCode.LINK subject: signUiId, linkType: "js", borderStyle: { width: 0, style: 5 }, color: "#2196f3", jsAction: `if(app.viewerType == 'DsPdfViewer') { var viewer = DsPdfViewer.findControl("${viewerSelector}"); viewer.__onSignatureLinkEvent(viewer, ${pageIndex}, "${signTitle}", "${signUiId}", ${rect[0]}, ${rect[1]}, ${(rect[2] - rect[0])}, ${(rect[3] - rect[1])}); }`, rect: rect, title: signTitle }; await viewer.addAnnotation(pageIndex, linkAnnotation, { skipPageRefresh: true }); } viewer.repaint(); } } function onSignatureLinkEvent(viewer, pageIndex, signTitle, signId, x, y, w, h) { viewer.showSignTool({ subject: "AddGraphicalSignature", tabs: ["Type", "Draw"], dialogLocation: "center", pageIndex: pageIndex, title: signTitle, location: { x: x, y: y }, canvasSize: { width: w * 5, height: h * 5 }, destinationScale: 1 / 5, convertToContent: true, afterAdd: function (result) { // remove current Sign UI and scroll to next one: removeSignature(viewer, signId).then(function () { viewer.addAnnotation(pageIndex, { annotationType: 3, borderStyle: { width: 0 }, fontSize: 10, contents: new Date().toLocaleDateString("en-US"), rect: [x + signDateLabelParams.offset_x, y + signDateLabelParams.offset_y, x + signDateLabelParams.offset_x + signDateLabelParams.w, y + signDateLabelParams.offset_y + signDateLabelParams.h], convertToContent: true }, { skipPageRefresh: false }).then(function () { scrollToNextSignature(viewer, signTitle); }); }); } }); } async function removeSignature(viewer, signId) { const annotations = await viewer.findAnnotations(signId, { findField: "subject", findAll: true }); for (let data of annotations) { await viewer.removeAnnotation(data.pageIndex, data.annotation.id); } }; async function scrollToNextSignature(viewer, signTitle) { const linkAnnotations = await viewer.findAnnotations(2, { findField: "annotationType", findAll: true }); const data = linkAnnotations.find((el) => el.annotation.title === signTitle) || linkAnnotations[0]; if(data) { viewer.scrollAnnotationIntoView(data.pageIndex, data.annotation, "smooth"); } else { alert("The document is fully signed."); } } })();