2024-06-29 00:43:08 +02:00
|
|
|
import { AccessTime, CalendarMonth, ExpandMore, Gesture, PictureAsPdf, Badge, Work } from '@mui/icons-material'
|
|
|
|
import { Box, Typography, Accordion, AccordionDetails, AccordionSummary, CircularProgress } from '@mui/material'
|
|
|
|
import styles from './style.module.scss'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
|
|
|
|
|
|
import * as PDFJS from "pdfjs-dist";
|
|
|
|
PDFJS.GlobalWorkerOptions.workerSrc = 'node_modules/pdfjs-dist/build/pdf.worker.mjs';
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
selectedFiles: any[]
|
|
|
|
}
|
|
|
|
|
|
|
|
interface PdfFile {
|
|
|
|
file: File,
|
|
|
|
pages: string[]
|
|
|
|
expanded?: boolean
|
|
|
|
}
|
|
|
|
|
|
|
|
interface DrawTool {
|
|
|
|
identifier: 'signature' | 'jobtitle' | 'fullname' | 'date' | 'datetime'
|
|
|
|
label: string
|
|
|
|
icon: JSX.Element,
|
|
|
|
defaultValue?: string
|
|
|
|
selected?: boolean
|
|
|
|
}
|
|
|
|
|
|
|
|
export const DrawPDFFields = (props: Props) => {
|
|
|
|
const { selectedFiles } = props
|
|
|
|
|
|
|
|
const [pdfFiles, setPdfFiles] = useState<PdfFile[]>([])
|
|
|
|
const [parsingPdf, setParsingPdf] = useState<boolean>(false)
|
|
|
|
const [showDrawToolBox, setShowDrawToolBox] = useState<boolean>(false)
|
|
|
|
|
|
|
|
const [selectedTool, setSelectedTool] = useState<DrawTool | null>()
|
|
|
|
const [toolbox] = useState<DrawTool[]>([
|
|
|
|
{
|
|
|
|
identifier: 'signature',
|
|
|
|
icon: <Gesture/>,
|
|
|
|
label: 'Signature'
|
|
|
|
|
|
|
|
},
|
|
|
|
{
|
|
|
|
identifier: 'fullname',
|
|
|
|
icon: <Badge/>,
|
|
|
|
label: 'Full Name'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
identifier: 'jobtitle',
|
|
|
|
icon: <Work/>,
|
|
|
|
label: 'Job Title'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
identifier: 'date',
|
|
|
|
icon: <CalendarMonth/>,
|
|
|
|
label: 'Date'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
identifier: 'datetime',
|
|
|
|
icon: <AccessTime/>,
|
|
|
|
label: 'Datetime'
|
|
|
|
},
|
|
|
|
])
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (selectedFiles) {
|
|
|
|
setParsingPdf(true)
|
|
|
|
|
|
|
|
parsePdfPages().finally(() => {
|
|
|
|
setParsingPdf(false)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}, [selectedFiles])
|
|
|
|
|
|
|
|
const parsePdfPages = async () => {
|
|
|
|
const pdfFiles: PdfFile[] = []
|
|
|
|
|
2024-06-29 00:44:06 +02:00
|
|
|
for (const file of selectedFiles) {
|
2024-06-29 00:43:08 +02:00
|
|
|
if (file.type.toLowerCase().includes('pdf')) {
|
|
|
|
const data = await readPdf(file)
|
|
|
|
const pages = await pdfToImages(data)
|
|
|
|
|
|
|
|
pdfFiles.push({
|
|
|
|
file: file,
|
|
|
|
pages: pages
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setPdfFiles(pdfFiles)
|
|
|
|
}
|
|
|
|
|
|
|
|
const getPdfPages = (pdfFile: PdfFile) => {
|
|
|
|
return (
|
|
|
|
<Box sx={{
|
|
|
|
width: '100%'
|
|
|
|
}}>
|
|
|
|
{pdfFile.pages.map((page: string) => {
|
|
|
|
return (
|
|
|
|
<div style={{
|
|
|
|
border: '1px solid #c4c4c4',
|
|
|
|
marginBottom: '10px'
|
2024-06-29 00:44:06 +02:00
|
|
|
}} className={`${styles.pdfImageWrapper} ${selectedTool ? styles.drawing : ''}`}>
|
2024-06-29 00:43:08 +02:00
|
|
|
<img draggable="false" style={{ width: '100%' }} src={page}/>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
})}
|
|
|
|
</Box>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Converts pdf to the images
|
|
|
|
* @param data pdf file bytes
|
|
|
|
*/
|
|
|
|
const pdfToImages = async (data: any): Promise<string[]> => {
|
|
|
|
const images: string[] = [];
|
|
|
|
const pdf = await PDFJS.getDocument(data).promise;
|
|
|
|
const canvas = document.createElement("canvas");
|
|
|
|
|
|
|
|
for (let i = 0; i < pdf.numPages; i++) {
|
|
|
|
const page = await pdf.getPage(i + 1);
|
|
|
|
const viewport = page.getViewport({ scale: 3 });
|
|
|
|
const context = canvas.getContext("2d");
|
|
|
|
canvas.height = viewport.height;
|
|
|
|
canvas.width = viewport.width;
|
|
|
|
await page.render({ canvasContext: context!, viewport: viewport }).promise;
|
|
|
|
images.push(canvas.toDataURL());
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.resolve(images)
|
|
|
|
}
|
|
|
|
|
|
|
|
const readPdf = (file: File) => {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
const reader = new FileReader();
|
|
|
|
|
|
|
|
reader.onload = (e: any) => {
|
|
|
|
const data = e.target.result
|
|
|
|
|
|
|
|
resolve(data)
|
|
|
|
};
|
|
|
|
|
|
|
|
reader.onerror = (err) => {
|
|
|
|
console.error('err', err)
|
|
|
|
reject(err)
|
|
|
|
};
|
|
|
|
|
|
|
|
reader.readAsDataURL(file);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
const hasExpandedPdf = () => {
|
|
|
|
return !!pdfFiles.filter(pdfFile => !!pdfFile.expanded).length
|
|
|
|
}
|
|
|
|
|
2024-06-29 00:46:35 +02:00
|
|
|
const handleAccordionExpandChange = (expanded: boolean, pdfFile: PdfFile) => {
|
2024-06-29 00:43:08 +02:00
|
|
|
pdfFile.expanded = expanded
|
|
|
|
|
|
|
|
setPdfFiles(pdfFiles)
|
|
|
|
setShowDrawToolBox(hasExpandedPdf())
|
|
|
|
}
|
|
|
|
|
|
|
|
const handleToolSelect = (drawTool: DrawTool) => {
|
|
|
|
// If clicked on the same tool, unselect
|
|
|
|
if (drawTool.identifier === selectedTool?.identifier) {
|
|
|
|
setSelectedTool(null)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
setSelectedTool(drawTool)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parsingPdf) {
|
|
|
|
return (
|
|
|
|
<Box sx={{ width: '100%', textAlign: 'center' }}>
|
|
|
|
<CircularProgress/>
|
|
|
|
</Box>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pdfFiles.length) {
|
|
|
|
return ''
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Box>
|
|
|
|
<Box sx={{ mt: 1 }}>
|
|
|
|
<Typography sx={{ mb: 1 }}>Draw fields on the PDFs:</Typography>
|
|
|
|
|
|
|
|
{pdfFiles.map((pdfFile) => {
|
|
|
|
return (
|
2024-06-29 00:46:35 +02:00
|
|
|
<Accordion expanded={pdfFile.expanded} onChange={(_event, expanded) => {handleAccordionExpandChange(expanded, pdfFile)}}>
|
2024-06-29 00:43:08 +02:00
|
|
|
<AccordionSummary
|
|
|
|
expandIcon={<ExpandMore />}
|
|
|
|
aria-controls="panel1-content"
|
|
|
|
id="panel1-header"
|
|
|
|
>
|
|
|
|
<PictureAsPdf sx={{ mr: 1 }}/>
|
|
|
|
{pdfFile.file.name}
|
|
|
|
</AccordionSummary>
|
|
|
|
<AccordionDetails>
|
|
|
|
{getPdfPages(pdfFile)}
|
|
|
|
</AccordionDetails>
|
|
|
|
</Accordion>
|
|
|
|
)
|
|
|
|
})}
|
|
|
|
</Box>
|
|
|
|
|
|
|
|
{showDrawToolBox && (
|
|
|
|
<Box className={styles.drawToolBoxContainer}>
|
|
|
|
<Box className={styles.drawToolBox}>
|
|
|
|
{toolbox.map((drawTool: DrawTool) => {
|
|
|
|
return (
|
|
|
|
<Box onClick={() => {handleToolSelect(drawTool)}} className={`${styles.toolItem} ${selectedTool?.identifier === drawTool.identifier ? styles.selected : ''}`}>
|
|
|
|
{ drawTool.icon }
|
|
|
|
{ drawTool.label }
|
|
|
|
</Box>
|
|
|
|
)
|
|
|
|
})}
|
|
|
|
</Box>
|
|
|
|
</Box>
|
|
|
|
)}
|
|
|
|
</Box>
|
|
|
|
)
|
|
|
|
}
|