First results converting receiver-ui-react into a Blazor Web App
This commit is contained in:
211
EnvelopeGenerator.ReceiverUIBlazor/wwwroot/js/pdfInterop.js
Normal file
211
EnvelopeGenerator.ReceiverUIBlazor/wwwroot/js/pdfInterop.js
Normal file
@@ -0,0 +1,211 @@
|
||||
(function () {
|
||||
const state = {
|
||||
pdfDoc: null,
|
||||
pdfBytes: null,
|
||||
lastViewport: null,
|
||||
};
|
||||
|
||||
function base64ToUint8(base64) {
|
||||
const binStr = atob(base64);
|
||||
const len = binStr.length;
|
||||
const bytes = new Uint8Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binStr.charCodeAt(i);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
async function reloadFromBase64(base64) {
|
||||
state.pdfBytes = base64ToUint8(base64);
|
||||
state.pdfDoc = await pdfjsLib.getDocument({ data: state.pdfBytes }).promise;
|
||||
return { pages: state.pdfDoc.numPages };
|
||||
}
|
||||
|
||||
function dataUrlDownload(dataUrl, filename) {
|
||||
const a = document.createElement('a');
|
||||
a.href = dataUrl;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
}
|
||||
|
||||
const pointerPads = new Map();
|
||||
|
||||
window.pdfInterop = {
|
||||
ensureReady: () => {
|
||||
if (pdfjsLib && pdfjsLib.GlobalWorkerOptions) {
|
||||
// worker is already loaded via CDN include
|
||||
return;
|
||||
}
|
||||
},
|
||||
loadPdf: async (base64) => {
|
||||
return await reloadFromBase64(base64);
|
||||
},
|
||||
renderPage: async (pageIndex, canvasId, targetWidth) => {
|
||||
if (!state.pdfDoc) {
|
||||
throw new Error('PDF not loaded');
|
||||
}
|
||||
const page = await state.pdfDoc.getPage(pageIndex + 1);
|
||||
const rawViewport = page.getViewport({ scale: 1 });
|
||||
const scale = targetWidth / rawViewport.width;
|
||||
const viewport = page.getViewport({ scale });
|
||||
|
||||
const canvas = document.getElementById(canvasId);
|
||||
if (!canvas) {
|
||||
throw new Error('Canvas not found');
|
||||
}
|
||||
const ctx = canvas.getContext('2d');
|
||||
canvas.width = viewport.width;
|
||||
canvas.height = viewport.height;
|
||||
|
||||
await page.render({ canvasContext: ctx, viewport }).promise;
|
||||
|
||||
state.lastViewport = {
|
||||
width: viewport.width,
|
||||
height: viewport.height,
|
||||
pageWidth: rawViewport.width,
|
||||
pageHeight: rawViewport.height,
|
||||
};
|
||||
|
||||
return state.lastViewport;
|
||||
},
|
||||
applySignature: async (payload) => {
|
||||
const {
|
||||
base64,
|
||||
pageIndex,
|
||||
left,
|
||||
top,
|
||||
width,
|
||||
height,
|
||||
renderWidth,
|
||||
renderHeight,
|
||||
dataUrl,
|
||||
autoDate,
|
||||
} = payload;
|
||||
|
||||
const pdfDoc = await PDFLib.PDFDocument.load(base64ToUint8(base64));
|
||||
const page = pdfDoc.getPage(pageIndex);
|
||||
const scaleX = page.getWidth() / renderWidth;
|
||||
const scaleY = page.getHeight() / renderHeight;
|
||||
|
||||
const pngImage = await pdfDoc.embedPng(dataUrl);
|
||||
const drawWidth = width * scaleX;
|
||||
const drawHeight = height * scaleY;
|
||||
const x = left * scaleX;
|
||||
const y = page.getHeight() - (top + height) * scaleY;
|
||||
|
||||
page.drawImage(pngImage, {
|
||||
x,
|
||||
y,
|
||||
width: drawWidth,
|
||||
height: drawHeight,
|
||||
});
|
||||
|
||||
if (autoDate) {
|
||||
const text = `Signed ${new Date().toLocaleString()}`;
|
||||
page.drawText(text, {
|
||||
x,
|
||||
y: y - 14 * scaleY,
|
||||
size: 14 * scaleX,
|
||||
color: PDFLib.rgb(0.07, 0.54, 0.26),
|
||||
});
|
||||
}
|
||||
|
||||
const updatedBase64 = await pdfDoc.saveAsBase64({ dataUri: false });
|
||||
await reloadFromBase64(updatedBase64);
|
||||
return updatedBase64;
|
||||
},
|
||||
applyText: async (payload) => {
|
||||
const {
|
||||
base64,
|
||||
pageIndex,
|
||||
left,
|
||||
top,
|
||||
width,
|
||||
height,
|
||||
renderWidth,
|
||||
renderHeight,
|
||||
text,
|
||||
fontSize,
|
||||
} = payload;
|
||||
|
||||
const pdfDoc = await PDFLib.PDFDocument.load(base64ToUint8(base64));
|
||||
const page = pdfDoc.getPage(pageIndex);
|
||||
const scaleX = page.getWidth() / renderWidth;
|
||||
const scaleY = page.getHeight() / renderHeight;
|
||||
|
||||
const x = left * scaleX;
|
||||
const y = page.getHeight() - (top + height) * scaleY;
|
||||
|
||||
page.drawText(text, {
|
||||
x,
|
||||
y,
|
||||
size: fontSize * scaleX,
|
||||
color: PDFLib.rgb(0.9, 0.9, 0.9),
|
||||
});
|
||||
|
||||
const updatedBase64 = await pdfDoc.saveAsBase64({ dataUri: false });
|
||||
await reloadFromBase64(updatedBase64);
|
||||
return updatedBase64;
|
||||
},
|
||||
downloadPdf: (base64, filename) => {
|
||||
dataUrlDownload(`data:application/pdf;base64,${base64}`, filename);
|
||||
},
|
||||
initSignaturePad: (canvasId) => {
|
||||
const canvas = document.getElementById(canvasId);
|
||||
if (!canvas) return;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.lineWidth = 2;
|
||||
ctx.lineCap = 'round';
|
||||
ctx.strokeStyle = '#22d3ee';
|
||||
|
||||
const padState = {
|
||||
drawing: false,
|
||||
lastX: 0,
|
||||
lastY: 0,
|
||||
};
|
||||
|
||||
function start(e) {
|
||||
padState.drawing = true;
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
padState.lastX = e.clientX - rect.left;
|
||||
padState.lastY = e.clientY - rect.top;
|
||||
}
|
||||
|
||||
function move(e) {
|
||||
if (!padState.drawing) return;
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left;
|
||||
const y = e.clientY - rect.top;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(padState.lastX, padState.lastY);
|
||||
ctx.lineTo(x, y);
|
||||
ctx.stroke();
|
||||
padState.lastX = x;
|
||||
padState.lastY = y;
|
||||
}
|
||||
|
||||
function end() {
|
||||
padState.drawing = false;
|
||||
}
|
||||
|
||||
canvas.onpointerdown = start;
|
||||
canvas.onpointermove = move;
|
||||
canvas.onpointerup = end;
|
||||
canvas.onpointerleave = end;
|
||||
|
||||
pointerPads.set(canvasId, { ctx, canvas });
|
||||
},
|
||||
clearSignaturePad: (canvasId) => {
|
||||
const pad = pointerPads.get(canvasId);
|
||||
if (!pad) return;
|
||||
pad.ctx.clearRect(0, 0, pad.canvas.width, pad.canvas.height);
|
||||
},
|
||||
getSignatureDataUrl: (canvasId) => {
|
||||
const pad = pointerPads.get(canvasId);
|
||||
if (!pad) return null;
|
||||
return pad.canvas.toDataURL('image/png');
|
||||
}
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user