|
|
|
@@ -75,13 +75,44 @@
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="col-12 text-center">
|
|
|
|
|
<canvas class="m-auto" id="canvas"></canvas>
|
|
|
|
|
<div v-if="multiPage" class="col-12 text-center">
|
|
|
|
|
<canvas
|
|
|
|
|
v-for="p in pageCount"
|
|
|
|
|
:key="p"
|
|
|
|
|
class="m-auto"
|
|
|
|
|
:id="`canvas-${p}`"
|
|
|
|
|
></canvas>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-else class="col-12 text-center">
|
|
|
|
|
<canvas class="m-auto" :id="canvas"></canvas>
|
|
|
|
|
</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 +121,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 +150,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 {
|
|
|
|
@@ -142,13 +178,15 @@ import {
|
|
|
|
|
|
|
|
|
|
pdfjsLib.GlobalWorkerOptions.workerSrc = "pdfjs-dist/build/pdf.worker.mjs";
|
|
|
|
|
|
|
|
|
|
const multiPage: Ref<boolean> = ref(true);
|
|
|
|
|
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 {
|
|
|
|
@@ -162,17 +200,30 @@ const $toast = useToast();
|
|
|
|
|
const signature = window.signature;
|
|
|
|
|
const urlInfo = build_download_info_link(signature.storedObject.filename);
|
|
|
|
|
|
|
|
|
|
console.log(signature);
|
|
|
|
|
|
|
|
|
|
const mountPdf = async (url: string) => {
|
|
|
|
|
const loadingTask = pdfjsLib.getDocument(url);
|
|
|
|
|
pdf = await loadingTask.promise;
|
|
|
|
|
pageCount.value = pdf.numPages;
|
|
|
|
|
await setPage(1);
|
|
|
|
|
if (multiPage.value) {
|
|
|
|
|
await setAllPages();
|
|
|
|
|
} else {
|
|
|
|
|
await setPage(1);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const getRenderContext = (pdfPage: PDFPageProxy) => {
|
|
|
|
|
const scale = 1;
|
|
|
|
|
const viewport = pdfPage.getViewport({ scale });
|
|
|
|
|
const canvas = document.querySelectorAll("canvas")[0] as HTMLCanvasElement;
|
|
|
|
|
let canvas;
|
|
|
|
|
if (multiPage.value) {
|
|
|
|
|
canvas = document.getElementById(
|
|
|
|
|
`canvas-${pdfPage.pageNumber}`
|
|
|
|
|
) as HTMLCanvasElement;
|
|
|
|
|
} else {
|
|
|
|
|
canvas = document.querySelectorAll("canvas")[0] as HTMLCanvasElement;
|
|
|
|
|
}
|
|
|
|
|
const context = canvas.getContext("2d") as CanvasRenderingContext2D;
|
|
|
|
|
canvas.height = viewport.height;
|
|
|
|
|
canvas.width = viewport.width;
|
|
|
|
@@ -183,6 +234,9 @@ const getRenderContext = (pdfPage: PDFPageProxy) => {
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const setAllPages = async () =>
|
|
|
|
|
Array.from(Array(pageCount.value).keys()).map((p) => setPage(p + 1));
|
|
|
|
|
|
|
|
|
|
const setPage = async (page: number) => {
|
|
|
|
|
const pdfPage = await pdf.getPage(page);
|
|
|
|
|
const renderContext = getRenderContext(pdfPage);
|
|
|
|
@@ -208,33 +262,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;
|
|
|
|
@@ -245,7 +297,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) => {
|
|
|
|
@@ -262,6 +314,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;
|
|
|
|
@@ -296,12 +355,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
|
|
|
|
@@ -309,16 +362,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);
|
|
|
|
@@ -420,21 +479,65 @@ 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();
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
#canvas {
|
|
|
|
|
canvas {
|
|
|
|
|
box-shadow: 0 20px 20px rgba(0, 0, 0, 0.1);
|
|
|
|
|
}
|
|
|
|
|
div#action-buttons {
|
|
|
|
|