@foreverrbum/ethsign
Version:
This package will allow you to electronically sign documents within your application
379 lines (340 loc) • 18.1 kB
JavaScript
import React, { useRef, useEffect, useState } from 'react';
import WebViewer from '@pdftron/webviewer';
import { storeNotif, truncate } from '../../helpers/dashboard';
import { FormattedMessage, useIntl } from 'react-intl';
import PrepareSignaturesField from '../PDF/PrepareSignatureFields';
import { updateXFDF, getDefaultTool } from '../../helpers/pdf';
const Prepare = (props) => {
const { ethAccount, ethAlias, handleWebviewer, file, filename, handleProgress, resubmit, signers, annotations, handleAnnotations, handleAnnotList, updateStoredData} = props;
const [instance, setInstance] = useState(null);
const [activeSigFieldTool, handleActiveSigFieldTool] = useState(null)
const { formatMessage, locale } = useIntl();
const viewer = useRef(null);
useEffect(() => {
if (file == null) {
storeNotif(formatMessage({id: 'NO_FILE_UPLOADED'}), formatMessage({id: 'PDF_NEEDS_TO_BE_UPLOADED_FIRST'}), 'danger')
}
WebViewer(
{
licenseKey: 'Buildblock Tech Pte. Ltd.:OEM:EthSign::B+:AMS(20220926):60A5E4AD0457F80A7360B13AC982537860615F858748CDEA9BF51DE6240C48AE4AB4B6F5C7',
path: '/webviewer/lib',
disabledElements: [
'header',
'toolsHeader',
]
},
viewer.current,
).then((instance) => {
if(locale=="zh"){
instance.setLanguage('zh_cn');
}
handleWebviewer(instance);
if (file != null) {
instance.loadDocument(file, { filename: filename.name+filename.ext })
}
const { Annotations, Tools, annotManager, docViewer, CoreControls } = instance;
instance.openElements(['leftPanel']);
annotManager.setCurrentUser(ethAlias? ethAlias:ethAccount)
// new code
signers.map((signer, index) => {
class SigFieldTxtAnnotation extends Annotations.CustomAnnotation {
constructor() {
super('SigFieldTxt'); // provide the custom XFDF element name
const page_idx = docViewer.getCurrentPage();
const rotation = docViewer.getCompleteRotation(page_idx)*-90;
this.PageRotation = rotation;
// TODO: add index in Sending.js
this.Subject = "SignatureField"
this.Signer = {
address: signer.address,
rotation: rotation,
alias: signer.alias,
avatar: signer.avatar
};
this.Index = 0;
this.Address = signer.alias? truncate(signer.alias) : truncate(signer.address);
this.Signed = false;
this.vertices = [];
const numVertices = 4;
for (let i = 0; i < numVertices; ++i) {
this.vertices.push(new CoreControls.Math.Point());
}
this.selectionModel = SigFieldSelectionModel;
}
serialize(element, pageMatrix) {
this.setCustomData('vertices', this.vertices);
const el = super.serialize(element, pageMatrix);
// create an attribute to save the vertices list
el.setAttribute('Signer', JSON.stringify(this.Signer));
el.setAttribute('Index', this.Index)
el.setAttribute('Address', this.Address)
el.setAttribute('Signed', this.Signed)
return el;
}
deserialize(element, pageMatrix) {
super.deserialize(element, pageMatrix);
this.Signer = JSON.parse(element.getAttribute('Signer'));
this.Index = element.getAttribute('Index')
this.Address = element.getAttribute('Address')
this.Signed = element.getAttribute('Signed')
const storedVertices = this.getCustomData('vertices');
this.vertices = storedVertices.map(v => new CoreControls.Math.Point(v.x, v.y)); }
draw(ctx, pageMatrix) {
this.setStyles(ctx, pageMatrix);
ctx.beginPath();
ctx.setLineDash([5,3]);
ctx.moveTo(this.vertices[0].x, this.vertices[0].y);
ctx.lineTo(this.vertices[1].x, this.vertices[1].y);
ctx.lineTo(this.vertices[2].x, this.vertices[2].y);
ctx.lineTo(this.vertices[3].x, this.vertices[3].y);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "black";
const zoom = docViewer.getZoom();
const textAnnot = this.Address+" Signs Here"
ctx.save()
ctx.textBaseline = 'middle';
ctx.textAlign = 'center';
ctx.rotate(this.PageRotation/180*Math.PI);
if (this.PageRotation == -90 || this.PageRotation == -270){
const textHeight = this.Height/11;
ctx.font = `${textHeight}px san-serif`;
if(this.PageRotation == -90){
ctx.fillText(textAnnot, -this.vertices[0].y-((this.Height/2)), this.vertices[0].x+(this.Width/2));
}else{
ctx.fillText(textAnnot, this.vertices[0].y+(this.Height/2), -this.vertices[0].x-(this.Width/2));
}
}
else{
const textHeight = this.Width/11;
ctx.font = `${textHeight}px san-serif`;
if(this.PageRotation == - 180){
ctx.fillText(textAnnot, -this.vertices[0].x+((this.Width/2)-this.Width), -this.vertices[0].y+(this.Height/2)-this.Height);
}
else{
ctx.fillText(textAnnot, this.vertices[0].x+(this.Width/2), this.vertices[0].y+this.Height/2);
}
}
ctx.stroke();
}
resize(rect) {
const annotRect = this.getRect();
const deltaX = rect.x1 - annotRect.x1;
const deltaY = rect.y1 - annotRect.y1;
this.vertices = this.vertices.map((vertex) => {
vertex.translate(deltaX, deltaY);
return vertex;
});
this.setRect(rect);
}
}
// this is necessary to set the elementName before instantiation
SigFieldTxtAnnotation.prototype.elementName = 'SigFieldTxt';
SigFieldTxtAnnotation.SerializationType = Annotations.CustomAnnotation.SerializationTypes.CUSTOM;
// register the annotation type so that it can be saved to XFDF files
annotManager.registerAnnotationType(SigFieldTxtAnnotation.prototype.elementName, SigFieldTxtAnnotation);
class SigFieldTxtCreateTool extends Tools.GenericAnnotationCreateTool {
constructor(docViewer) {
// TriangleAnnotation is the clßßass (function) for our annotation we defined previously
super(docViewer, SigFieldTxtAnnotation);
}
mouseLeftDown(e) {
super.mouseLeftDown(e);
if (this.annotation) {
const zoom = docViewer.getZoom();
this.annotation.Width=68/zoom;
this.annotation.Height=34/zoom;
this.annotation.Y = this.annotation.Y - this.annotation.Height;
this.annotation.vertices[0].x = this.annotation.X;
this.annotation.vertices[0].y = this.annotation.Y;
this.annotation.vertices[1].x = this.annotation.X + this.annotation.Width;
this.annotation.vertices[1].y = this.annotation.Y;
this.annotation.vertices[2].x = this.annotation.X + this.annotation.Width;
this.annotation.vertices[2].y = this.annotation.Y + this.annotation.Height;
this.annotation.vertices[3].x = this.annotation.X;
this.annotation.vertices[3].y = this.annotation.Y + this.annotation.Height;
// update the annotation appearance
annotManager.redrawAnnotation(this.annotation); }
}
mouseMove(e) {
super.mouseMove(e);
if (this.annotation) {
const zoom = docViewer.getZoom();
const initialHeight = 34/zoom;
const intiialWidth = 68/zoom;
this.annotation.Width = this.annotation.Width + intiialWidth;
this.annotation.Height = this.annotation.Height + initialHeight
this.annotation.Y = this.annotation.Y - initialHeight;
this.annotation.vertices[0].x = this.annotation.X;
this.annotation.vertices[0].y = this.annotation.Y;
this.annotation.vertices[1].x = this.annotation.X + this.annotation.Width;
this.annotation.vertices[1].y = this.annotation.Y;
this.annotation.vertices[2].x = this.annotation.X + this.annotation.Width;
this.annotation.vertices[2].y = this.annotation.Y + this.annotation.Height;
this.annotation.vertices[3].x = this.annotation.X;
this.annotation.vertices[3].y = this.annotation.Y + this.annotation.Height;
SigFieldTxtTool.cursor="none"
// update the annotation appearance
annotManager.redrawAnnotation(this.annotation);
}
}
};
class SigFieldControlHandle extends Annotations.ControlHandle {
constructor(annotation, index) {
super();
this.annotation = annotation;
this.index = index;
}
getDimensions(annotation, selectionBox, zoom) {
let x = annotation.vertices[this.index].x;
let y = annotation.vertices[this.index].y;
const width = Annotations.ControlHandle.handleWidth / zoom;
const height = Annotations.ControlHandle.handleHeight / zoom;
let minX = Number.MAX_VALUE;
let maxX = -Number.MAX_VALUE;
let minY = Number.MAX_VALUE;
let maxY = -Number.MAX_VALUE;
for (let i = 0; i < annotation.vertices.length; ++i) {
const vertex = annotation.vertices[i];
minX = Math.min(minX, vertex.x);
maxX = Math.max(maxX, vertex.x);
minY = Math.min(minY, vertex.y);
maxY = Math.max(maxY, vertex.y);
}
x -= width * 0.5;
y -= height * 0.5;
return new CoreControls.Math.Rect(x, y, x + width, y + height);
}
move(annotation, deltaX, deltaY, fromPoint, toPoint) {
annotation.vertices[this.index].x += deltaX;
annotation.vertices[this.index].y += deltaY;
let minX = Number.MAX_VALUE;
let maxX = -Number.MAX_VALUE;
let minY = Number.MAX_VALUE;
let maxY = -Number.MAX_VALUE;
if(this.index==0){
annotation.vertices[1].y += deltaY;
annotation.vertices[3].x += deltaX;
}
else if(this.index==1){
annotation.vertices[0].y += deltaY;
annotation.vertices[2].x += deltaX;
}
else if(this.index==2){
annotation.vertices[3].y += deltaY;
annotation.vertices[1].x += deltaX;
}
else if(this.index==3){
annotation.vertices[2].y += deltaY;
annotation.vertices[0].x += deltaX;
}
for (let i = 0; i < annotation.vertices.length; ++i) {
const vertex = annotation.vertices[i];
minX = Math.min(minX, vertex.x);
maxX = Math.max(maxX, vertex.x);
minY = Math.min(minY, vertex.y);
maxY = Math.max(maxY, vertex.y);
}
annotation.vertices[0].x = minX;
annotation.vertices[0].y = minY;
annotation.vertices[1].x = maxX;
annotation.vertices[1].y = minY;
annotation.vertices[2].x = maxX;
annotation.vertices[2].y = maxY;
annotation.vertices[3].x = minX;
annotation.vertices[3].y = maxY;
const rect = new Annotations.Rect(minX, minY, maxX, maxY);
annotation.setRect(rect);
return true;
}
}
class SigFieldSelectionModel extends Annotations.SelectionModel {
constructor(annotation, canModify) {
super(annotation, canModify);
if (canModify) {
const controlHandles = this.getControlHandles();
controlHandles.push(new SigFieldControlHandle(annotation, 0));
controlHandles.push(new SigFieldControlHandle(annotation, 1));
controlHandles.push(new SigFieldControlHandle(annotation, 2));
controlHandles.push(new SigFieldControlHandle(annotation, 3));
}
}
}
const SigFieldTxtToolName = 'AnnotationCreateSigFieldTxt' + index;
const SigFieldTxtTool = new SigFieldTxtCreateTool(docViewer);
SigFieldTxtTool.cursor = "url(/files/sign_here_68.png) 0 68,url(/files/sign_here_32.png) 0 32,crosshair"
instance.registerTool({
toolName: SigFieldTxtToolName,
toolObject: SigFieldTxtTool,
buttonImage: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor">' +
'<path d="M12 7.77L18.39 18H5.61L12 7.77M12 4L2 20h20L12 4z"/>' +
'<path fill="none" d="M0 0h24v24H0V0z"/>' +
'</svg>',
buttonName: 'SigFieldTxtToolButton',
tooltip: 'SigFieldTxt'
}, SigFieldTxtAnnotation);
instance.setHeaderItems((header) => {
header.getHeader('toolbarGroup-Shapes').get('freeHandToolGroupButton').insertBefore({
type: 'toolButton',
toolName: SigFieldTxtToolName
});
});
});
annotManager.on('annotationChanged', async (annotations, action, { imported }) => {
if (imported) {
const annotList = instance.annotManager.getAnnotationsList();
handleAnnotList(annotList)
return;
}else{
const annotList = instance.annotManager.getAnnotationsList();
handleAnnotList(annotList)
await updateXFDF(instance, annotList, handleAnnotations, updateStoredData);
if(action == 'add'){
const signatures = annotations.filter(annot => annot.Subject == "SignatureField")
if(signatures.length>0){
const curTool = docViewer.getToolMode()
curTool.cursor = "url(/files/sign_here_68.png) 0 68,url(/files/sign_here_32.png) 0 32,crosshair"
}
instance.setToolMode(getDefaultTool());
handleActiveSigFieldTool(null)
annotManager.selectAnnotations(annotations)
}
}
});
docViewer.on('documentLoaded', () => {
instance.annotManager.importAnnotations(annotations)
setInstance(instance)
});
});
}, []);
return (
<>
<div className="bg-gray-400 flex-grow flex flex-col">
<div className="mx-auto flex-grow flex flex h-full w-full text-gray-300 text-15">
<div className="flex-grow" ref={viewer}>
</div>
<PrepareSignaturesField instance={instance} signers={signers} handleActiveSigFieldTool={handleActiveSigFieldTool} activeSigFieldTool={activeSigFieldTool} ethAccount={ethAccount}/>
</div>
</div>
<nav className="box-shadow-footer z-10 sticky bottom-0 bg-gray-40">
<div className="max-w-7xl mx-auto px-2 ">
<div className="relative flex items-center justify-between h-16">
<div className="flex w-full">
<div className="flex-1"></div>
<div className="flex flex-grow justify-center items-center text-gray-80 font-medium" >{filename && filename.name + filename.ext}</div>
<div className="select-none flex flex-1 justify-end">
<button className="focus:outline-none text-white mr-5 w-28 py-2 font-medium bg-gray-300 hover:bg-gray-600 rounded-sm" onClick={() => { handleProgress(resubmit ? 0 : 1) }}>
<FormattedMessage id='BACK' />
</button>
<button className="select-none focus:outline-none text-white w-28 py-2 font-medium bg-orange-500 hover:bg-orange-600 rounded-sm" onClick={() => { handleProgress(resubmit ? 2 : 3) }}>
<FormattedMessage id='NEXT' />
</button>
</div>
</div>
</div>
</div>
</nav>
</>
);
}
export default Prepare;