順番にPDFに電子署名する

このサンプルでは、2人のユーザー(「購入者」と「販売者」)がそれぞれ複数の署名箇所に電子署名する必要がある PDF(「契約書」)を表示しています。 config.js のコードにより、ユーザーがドキュメントに署名する際に、すべての署名箇所へ順番に移動します。

window.onload = function() { const viewer = new DsPdfViewer("#viewer", getViewerOptions()); configureViewerUI(viewer); viewer.open("/diodocs/pdfviewer/demos/product-bundles/assets/pdf/sign-pdf-in-order.pdf"); }
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>順番にPDFに電子署名</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="./src/styles.css"> <script src="/diodocs/pdfviewer/demos/product-bundles/build/dspdfviewer.js"></script> <script src="/diodocs/pdfviewer/demos/product-bundles/build/wasmSupportApi.js"></script> <script src="/diodocs/pdfviewer/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. "multiple-sign-a-pdf-in-predefined-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: getSupportApiSettings(), 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) { let linkAnnotations = await viewer.findAnnotations(signTitle, { findField: "title", findAll: false }); if (linkAnnotations.length === 0) linkAnnotations = await viewer.findAnnotations(2, { findField: "annotationType", findAll: false }); if (linkAnnotations.length > 0) { var linkAnnotationInfo = linkAnnotations[0].annotation.title !== signTitle && linkAnnotations[1] && linkAnnotations[1].annotation.title === signTitle ? linkAnnotations[1] : linkAnnotations[0]; let annotationElement = viewer.scrollView.querySelector(`section.linkAnnotation[data-annotation-id='${linkAnnotationInfo.id}']`); if (annotationElement) { annotationElement.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "nearest" }); } else { pageElement = viewer.scrollView.querySelector(`div.page[data-index='${linkAnnotationInfo.pageIndex}']`); if (pageElement) { pageElement.scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" }); setTimeout(function () { viewer.repaint([linkAnnotationInfo.pageIndex]); }, 400); } } if (linkAnnotationInfo.pageIndex < viewer.pageCount - 1) { viewer.repaint([linkAnnotationInfo.pageIndex + 1]); } } else { alert("The document is fully signed."); } } })();