Signature: add a signature zone manually

This commit is contained in:
nobohan 2024-09-04 10:14:08 +02:00 committed by Julien Fastré
parent 18af2ca70b
commit c23568032c
Signed by: julienfastre
GPG Key ID: BDE2190974723FCB
3 changed files with 128 additions and 43 deletions

View File

@ -83,7 +83,7 @@ export interface PDFPage {
height: number,
}
export interface SignatureZone {
index: number,
index: number | null,
x: number,
y: number,
width: number,
@ -98,3 +98,5 @@ export interface Signature {
}
export type SignedState = 'pending' | 'signed' | 'rejected' | 'canceled' | 'error';
export type CanvasEvent = 'select' | 'add';

View File

@ -80,8 +80,32 @@
</div>
<div class="col-12 p-4" id="action-buttons" v-if="signedState !== 'signed'">
<div class="row mb-3">
<div class="col-12 d-flex justify-content-end">
<div class="col-4 col-xl-3 gap-2 d-grid">
<button
v-if="adding"
class="btn btn-misc btn-cancel me-2 btn-sm"
@click="removeNewZone()"
>
{{ $t("remove_sign_zone") }}
</button>
</div>
<div class="col-4 gap-2 d-grid">
<button
class="btn btn-create btn-sm"
:class="{ active: canvasEvent === 'add' }"
@click="toggleAddZone()"
>
{{ $t("add_sign_zone") }}
</button>
</div>
</div>
</div>
<div class="row">
<div class="col-6">
<div class="col-4">
<button
class="btn btn-action me-2"
:disabled="!userSignatureZone"
@ -90,7 +114,7 @@
{{ $t("sign") }}
</button>
</div>
<div class="col-6 d-flex justify-content-end">
<div class="col-8 d-flex justify-content-end">
<button
class="btn btn-misc me-2"
:hidden="!userSignatureZone"
@ -119,7 +143,12 @@
import { ref, Ref, reactive } from "vue";
import { useToast } from "vue-toast-notification";
import "vue-toast-notification/dist/theme-sugar.css";
import { Signature, SignatureZone, SignedState } from "../../types";
import {
CanvasEvent,
Signature,
SignatureZone,
SignedState,
} from "../../types";
import { makeFetch } from "../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
import * as pdfjsLib from "pdfjs-dist";
import {
@ -143,11 +172,12 @@ pdfjsLib.GlobalWorkerOptions.workerSrc = "pdfjs-dist/build/pdf.worker.mjs";
const modalOpen: Ref<boolean> = ref(false);
const loading: Ref<boolean> = ref(false);
const adding: Ref<boolean> = ref(false);
const canvasEvent: Ref<CanvasEvent> = ref("select");
const signedState: Ref<SignedState> = ref("pending");
const page: Ref<number> = ref(1);
const pageCount: Ref<number> = ref(0);
let userSignatureZone: Ref<null | SignatureZone> = ref(null);
let pdfSource: Ref<string> = ref("");
let pdf = {} as PDFDocumentProxy;
declare global {
@ -160,6 +190,8 @@ const $toast = useToast();
const signature = window.signature;
console.log(signature);
const mountPdf = async (url: string) => {
const loadingTask = pdfjsLib.getDocument(url);
pdf = await loadingTask.promise;
@ -202,33 +234,31 @@ async function downloadAndOpen(): Promise<Blob> {
const initPdf = () => {
const canvas = document.querySelectorAll("canvas")[0] as HTMLCanvasElement;
canvas.addEventListener(
"pointerup",
(e: PointerEvent) => canvasClick(e, canvas),
false
);
canvas.addEventListener("pointerup", canvasClick, false);
setTimeout(() => addZones(page.value), 800);
};
const scaleXToCanvas = (x: number, canvasWidth: number, PDFWidth: number) =>
Math.round((x * canvasWidth) / PDFWidth);
const scaleYToCanvas = (h: number, canvasHeight: number, PDFHeight: number) =>
Math.round((h * canvasHeight) / PDFHeight);
const hitSignature = (
zone: SignatureZone,
xy: number[],
canvasWidth: number,
canvasHeight: number
) => {
const scaleXToCanvas = (x: number) =>
Math.round((x * canvasWidth) / zone.PDFPage.width);
const scaleHeightToCanvas = (h: number) =>
Math.round((h * canvasHeight) / zone.PDFPage.height);
const scaleYToCanvas = (y: number) =>
Math.round(zone.PDFPage.height - scaleHeightToCanvas(y));
return (
scaleXToCanvas(zone.x) < xy[0] &&
xy[0] < scaleXToCanvas(zone.x + zone.width) &&
scaleYToCanvas(zone.y) < xy[1] &&
xy[1] < scaleYToCanvas(zone.y) + scaleHeightToCanvas(zone.height)
);
};
) =>
scaleXToCanvas(zone.x, canvasWidth, zone.PDFPage.width) < xy[0] &&
xy[0] <
scaleXToCanvas(zone.x + zone.width, canvasWidth, zone.PDFPage.width) &&
zone.PDFPage.height -
scaleYToCanvas(zone.y, canvasHeight, zone.PDFPage.height) <
xy[1] &&
xy[1] <
scaleYToCanvas(zone.height - zone.y, canvasHeight, zone.PDFPage.height) +
zone.PDFPage.height;
const selectZone = (z: SignatureZone, canvas: HTMLCanvasElement) => {
userSignatureZone.value = z;
@ -239,7 +269,7 @@ const selectZone = (z: SignatureZone, canvas: HTMLCanvasElement) => {
}
};
const canvasClick = (e: PointerEvent, canvas: HTMLCanvasElement) =>
const selectZoneOnCanvas = (e: PointerEvent, canvas: HTMLCanvasElement) =>
signature.zones
.filter((z) => z.PDFPage.index + 1 === page.value)
.map((z) => {
@ -256,6 +286,13 @@ const canvasClick = (e: PointerEvent, canvas: HTMLCanvasElement) =>
}
});
const canvasClick = (e: PointerEvent) => {
const canvas = document.querySelectorAll("canvas")[0] as HTMLCanvasElement;
canvasEvent.value === "select"
? selectZoneOnCanvas(e, canvas)
: addZoneOnCanvas(e, canvas);
};
const turnPage = async (upOrDown: number) => {
userSignatureZone.value = null;
page.value = page.value + upOrDown;
@ -290,12 +327,6 @@ const drawZone = (
) => {
const unselectedBlue = "#007bff";
const selectedBlue = "#034286";
const scaleXToCanvas = (x: number) =>
Math.round((x * canvasWidth) / zone.PDFPage.width);
const scaleHeightToCanvas = (h: number) =>
Math.round((h * canvasHeight) / zone.PDFPage.height);
const scaleYToCanvas = (y: number) =>
Math.round(zone.PDFPage.height - scaleHeightToCanvas(y));
ctx.strokeStyle =
userSignatureZone.value?.index === zone.index
? selectedBlue
@ -303,16 +334,22 @@ const drawZone = (
ctx.lineWidth = 2;
ctx.lineJoin = "bevel";
ctx.strokeRect(
scaleXToCanvas(zone.x),
scaleYToCanvas(zone.y),
scaleXToCanvas(zone.width),
scaleHeightToCanvas(zone.height)
scaleXToCanvas(zone.x, canvasWidth, zone.PDFPage.width),
zone.PDFPage.height -
scaleYToCanvas(zone.y, canvasHeight, zone.PDFPage.height),
scaleXToCanvas(zone.width, canvasWidth, zone.PDFPage.width),
scaleYToCanvas(zone.height, canvasHeight, zone.PDFPage.height)
);
ctx.font = "bold 16px serif";
ctx.textAlign = "center";
ctx.fillStyle = "black";
const xText = scaleXToCanvas(zone.x) + scaleXToCanvas(zone.width) / 2;
const yText = scaleYToCanvas(zone.y) + scaleHeightToCanvas(zone.height) / 2;
const xText =
scaleXToCanvas(zone.x, canvasWidth, zone.PDFPage.width) +
scaleXToCanvas(zone.width, canvasWidth, zone.PDFPage.width) / 2;
const yText =
zone.PDFPage.height -
scaleYToCanvas(zone.y, canvasHeight, zone.PDFPage.height) +
scaleYToCanvas(zone.height, canvasHeight, zone.PDFPage.height) / 2;
if (userSignatureZone.value?.index === zone.index) {
ctx.fillStyle = selectedBlue;
ctx.fillText("Signer ici", xText, yText);
@ -414,14 +451,58 @@ const confirmSign = () => {
};
const undoSign = async () => {
// const canvas = document.querySelectorAll("canvas")[0];
// const ctx = canvas.getContext("2d");
// if (ctx && userSignatureZone.value) {
// //drawZone(userSignatureZone.value, ctx, canvas.width, canvas.height);
// }
signature.zones = signature.zones.filter((z) => z.index !== null);
await setPage(page.value);
setTimeout(() => addZones(page.value), 200);
userSignatureZone.value = null;
adding.value = false;
};
const toggleAddZone = () => {
canvasEvent.value === "select"
? (canvasEvent.value = "add")
: (canvasEvent.value = "select");
};
const addZoneOnCanvas = (e: PointerEvent, canvas: HTMLCanvasElement) => {
const BOX_WIDTH = 180;
const BOX_HEIGHT = 90;
const PDFPageHeight = canvas.height;
const PDFPageWidth = canvas.width;
const x = e.offsetX;
const y = e.offsetY;
const newZone: SignatureZone = {
index: null,
x:
scaleXToCanvas(x, canvas.width, PDFPageWidth) -
scaleXToCanvas(BOX_WIDTH / 2, canvas.width, PDFPageWidth),
y:
PDFPageHeight -
scaleYToCanvas(y, canvas.height, PDFPageHeight) +
scaleYToCanvas(BOX_HEIGHT / 2, canvas.height, PDFPageHeight),
width: BOX_WIDTH,
height: BOX_HEIGHT,
PDFPage: {
index: page.value - 1,
width: PDFPageWidth,
height: PDFPageHeight,
},
};
signature.zones.push(newZone);
setTimeout(() => addZones(page.value), 200);
canvasEvent.value = "select";
adding.value = true;
};
const removeNewZone = async () => {
signature.zones = signature.zones.filter((z) => z.index !== null);
userSignatureZone.value = null;
await setPage(page.value);
setTimeout(() => addZones(page.value), 200);
canvasEvent.value = "select";
adding.value = false;
};
downloadAndOpen();

View File

@ -16,7 +16,9 @@ const appMessages = {
last_sign_zone: 'Zone de signature précédente',
next_sign_zone: 'Zone de signature suivante',
electronic_signature_in_progress: 'Signature électronique en cours...',
loading: 'Chargement...'
loading: 'Chargement...',
add_sign_zone: 'Ajouter une zone de signature',
remove_sign_zone: 'Enlever la zone',
}
}