311 lines
9.9 KiB
TypeScript
311 lines
9.9 KiB
TypeScript
import { saveAs } from 'file-saver';
|
|
import jsPDF from 'jspdf';
|
|
|
|
export interface CustomerInfo {
|
|
firstName: string;
|
|
lastName: string;
|
|
postalAddress: string;
|
|
email: string;
|
|
}
|
|
|
|
export interface NotaryInfo {
|
|
name: string;
|
|
// Add more notary fields as needed
|
|
}
|
|
|
|
export interface Metadata {
|
|
fileName: string,
|
|
createdAt: Date,
|
|
isDeleted: boolean,
|
|
updatedAt: Date,
|
|
commitmentId: string,
|
|
documentUid: string;
|
|
documentType: string;
|
|
}
|
|
|
|
export interface CertificateData {
|
|
customer: CustomerInfo;
|
|
notary: NotaryInfo;
|
|
folderUid: string;
|
|
documentHash: string;
|
|
metadata: Metadata;
|
|
}
|
|
|
|
export default class PdfService {
|
|
private static instance: PdfService;
|
|
|
|
public static getInstance(): PdfService {
|
|
if (!PdfService.instance) {
|
|
PdfService.instance = new PdfService();
|
|
}
|
|
return PdfService.instance;
|
|
}
|
|
|
|
/**
|
|
* Generate a certificate PDF for a document
|
|
* @param certificateData - Data needed for the certificate
|
|
* @returns Promise<Blob> - The generated PDF as a blob
|
|
*/
|
|
public async generateCertificate(certificateData: CertificateData): Promise<Blob> {
|
|
try {
|
|
const doc = new jsPDF();
|
|
|
|
// Notary Information Section (Top Left)
|
|
doc.setFontSize(12);
|
|
doc.setFont('helvetica', 'bold');
|
|
doc.text('Notaire Validateur:', 20, 30);
|
|
doc.setFont('helvetica', 'normal');
|
|
doc.text(certificateData.notary.name, 20, 37);
|
|
|
|
// Customer Information Section (Top Right)
|
|
doc.setFont('helvetica', 'bold');
|
|
doc.text('Fournisseur de Document:', 120, 30);
|
|
doc.setFont('helvetica', 'normal');
|
|
doc.text(`${certificateData.customer.firstName} ${certificateData.customer.lastName}`, 120, 37);
|
|
doc.text(certificateData.customer.email, 120, 44);
|
|
doc.text(certificateData.customer.postalAddress, 120, 51);
|
|
|
|
// Centered Title
|
|
doc.setFontSize(20);
|
|
doc.setFont('helvetica', 'bold');
|
|
doc.text('Certificat de Validation de Document', 105, 80, { align: 'center' });
|
|
|
|
// Add a line separator
|
|
doc.setLineWidth(0.5);
|
|
doc.line(20, 90, 190, 90);
|
|
|
|
// Reset font for content
|
|
doc.setFontSize(12);
|
|
doc.setFont('helvetica', 'normal');
|
|
|
|
let yPosition = 110;
|
|
|
|
// Document Information Section
|
|
doc.setFont('helvetica', 'bold');
|
|
doc.text('Informations du Document', 20, yPosition);
|
|
yPosition += 10;
|
|
|
|
doc.setFont('helvetica', 'normal');
|
|
doc.text(`UID du Document: ${certificateData.metadata.documentUid}`, 20, yPosition);
|
|
yPosition += 7;
|
|
doc.text(`Type de Document: ${certificateData.metadata.documentType}`, 20, yPosition);
|
|
yPosition += 7;
|
|
doc.text(`UID du Dossier: ${certificateData.folderUid}`, 20, yPosition);
|
|
yPosition += 7;
|
|
doc.text(`Créé le: ${certificateData.metadata.createdAt.toLocaleDateString('fr-FR')}`, 20, yPosition);
|
|
yPosition += 7;
|
|
doc.text(`Nom du fichier: ${certificateData.metadata.fileName}`, 20, yPosition);
|
|
yPosition += 7;
|
|
doc.text(`ID de transaction: ${certificateData.metadata.commitmentId}`, 20, yPosition);
|
|
yPosition += 7;
|
|
doc.text(`Dernière modification: ${certificateData.metadata.updatedAt.toLocaleDateString('fr-FR')}`, 20, yPosition);
|
|
yPosition += 7;
|
|
doc.text(`Statut: ${certificateData.metadata.isDeleted ? 'Supprimé' : 'Actif'}`, 20, yPosition);
|
|
yPosition += 15;
|
|
|
|
// Document Hash Section
|
|
doc.setFont('helvetica', 'bold');
|
|
doc.text('Intégrité du Document', 20, yPosition);
|
|
yPosition += 10;
|
|
|
|
doc.setFont('helvetica', 'normal');
|
|
doc.text(`Hash SHA256 du Document:`, 20, yPosition);
|
|
yPosition += 7;
|
|
|
|
// Split hash into multiple lines if needed
|
|
const hash = certificateData.documentHash;
|
|
const maxWidth = 170; // Available width for text
|
|
const hashLines = this.splitTextToFit(doc, hash, maxWidth);
|
|
|
|
for (const line of hashLines) {
|
|
doc.text(line, 25, yPosition);
|
|
yPosition += 7;
|
|
}
|
|
yPosition += 8; // Extra space after hash
|
|
|
|
// Footer
|
|
const pageCount = doc.getNumberOfPages();
|
|
for (let i = 1; i <= pageCount; i++) {
|
|
doc.setPage(i);
|
|
doc.setFontSize(10);
|
|
doc.setFont('helvetica', 'italic');
|
|
doc.text(`Page ${i} sur ${pageCount}`, 105, 280, { align: 'center' });
|
|
doc.text(`Généré le ${new Date().toLocaleString('fr-FR')}`, 105, 285, { align: 'center' });
|
|
}
|
|
|
|
return doc.output('blob');
|
|
} catch (error) {
|
|
console.error('Error generating certificate:', error);
|
|
throw new Error('Failed to generate certificate');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Split text to fit within a specified width
|
|
* @param doc - jsPDF document instance
|
|
* @param text - Text to split
|
|
* @param maxWidth - Maximum width in points
|
|
* @returns Array of text lines
|
|
*/
|
|
private splitTextToFit(doc: jsPDF, text: string, maxWidth: number): string[] {
|
|
const lines: string[] = [];
|
|
let currentLine = '';
|
|
|
|
// Split by words first
|
|
const words = text.split('');
|
|
|
|
for (const char of words) {
|
|
const testLine = currentLine + char;
|
|
const testWidth = doc.getTextWidth(testLine);
|
|
|
|
if (testWidth <= maxWidth) {
|
|
currentLine = testLine;
|
|
} else {
|
|
if (currentLine) {
|
|
lines.push(currentLine);
|
|
currentLine = char;
|
|
} else {
|
|
// If even a single character is too wide, force a break
|
|
lines.push(char);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (currentLine) {
|
|
lines.push(currentLine);
|
|
}
|
|
|
|
return lines;
|
|
}
|
|
|
|
/**
|
|
* Download a certificate PDF
|
|
* @param certificateData - Data needed for the certificate
|
|
* @param filename - Optional filename for the download
|
|
*/
|
|
public async downloadCertificate(certificateData: CertificateData, filename?: string): Promise<void> {
|
|
try {
|
|
const pdfBlob = await this.generateCertificate(certificateData);
|
|
const defaultFilename = `certificate_${certificateData.metadata.documentUid}_${new Date().toISOString().split('T')[0]}.pdf`;
|
|
saveAs(pdfBlob, filename || defaultFilename);
|
|
} catch (error) {
|
|
console.error('Error downloading certificate:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate certificate for notary documents
|
|
* @param certificateData - Data needed for the certificate
|
|
* @returns Promise<Blob> - The generated PDF as a blob
|
|
*/
|
|
public async generateNotaryCertificate(certificateData: CertificateData): Promise<Blob> {
|
|
try {
|
|
const doc = new jsPDF();
|
|
|
|
// Notary Information Section (Top Left)
|
|
doc.setFontSize(12);
|
|
doc.setFont('helvetica', 'bold');
|
|
doc.text('Notaire Validateur:', 20, 30);
|
|
doc.setFont('helvetica', 'normal');
|
|
doc.text(certificateData.notary.name, 20, 37);
|
|
|
|
// Customer Information Section (Top Right)
|
|
doc.setFont('helvetica', 'bold');
|
|
doc.text('Fournisseur de Document:', 120, 30);
|
|
doc.setFont('helvetica', 'normal');
|
|
doc.text(`${certificateData.customer.firstName} ${certificateData.customer.lastName}`, 120, 37);
|
|
doc.text(certificateData.customer.email, 120, 44);
|
|
doc.text(certificateData.customer.postalAddress, 120, 51);
|
|
|
|
// Centered Title
|
|
doc.setFontSize(20);
|
|
doc.setFont('helvetica', 'bold');
|
|
doc.text('Certificat Notarial de Validation de Document', 105, 80, { align: 'center' });
|
|
|
|
// Add a line separator
|
|
doc.setLineWidth(0.5);
|
|
doc.line(20, 90, 190, 90);
|
|
|
|
// Reset font for content
|
|
doc.setFontSize(12);
|
|
doc.setFont('helvetica', 'normal');
|
|
|
|
let yPosition = 110;
|
|
|
|
// Document Information Section
|
|
doc.setFont('helvetica', 'bold');
|
|
doc.text('Informations du Document', 20, yPosition);
|
|
yPosition += 10;
|
|
|
|
doc.setFont('helvetica', 'normal');
|
|
doc.text(`Identifiant du Document: ${certificateData.metadata.documentUid}`, 20, yPosition);
|
|
yPosition += 7;
|
|
doc.text(`Type de Document: ${certificateData.metadata.documentType}`, 20, yPosition);
|
|
yPosition += 7;
|
|
doc.text(`Numéro de dossier: ${certificateData.folderUid}`, 20, yPosition);
|
|
yPosition += 7;
|
|
doc.text(`Créé le: ${certificateData.metadata.createdAt.toLocaleDateString('fr-FR')}`, 20, yPosition);
|
|
yPosition += 7;
|
|
doc.text(`Nom du fichier: ${certificateData.metadata.fileName}`, 20, yPosition);
|
|
yPosition += 7;
|
|
doc.text(`ID de transaction: ${certificateData.metadata.commitmentId}`, 20, yPosition);
|
|
yPosition += 7;
|
|
doc.text(`Dernière modification: ${certificateData.metadata.updatedAt.toLocaleDateString('fr-FR')}`, 20, yPosition);
|
|
yPosition += 7;
|
|
doc.text(`Statut: ${certificateData.metadata.isDeleted ? 'Supprimé' : 'Actif'}`, 20, yPosition);
|
|
yPosition += 15;
|
|
|
|
// Document Hash Section
|
|
doc.setFont('helvetica', 'bold');
|
|
doc.text('Intégrité du Document', 20, yPosition);
|
|
yPosition += 10;
|
|
|
|
doc.setFont('helvetica', 'normal');
|
|
doc.text(`Hash SHA256 du Document:`, 20, yPosition);
|
|
yPosition += 7;
|
|
|
|
// Split hash into multiple lines if needed
|
|
const hash = certificateData.documentHash;
|
|
const maxWidth = 170; // Available width for text
|
|
const hashLines = this.splitTextToFit(doc, hash, maxWidth);
|
|
|
|
for (const line of hashLines) {
|
|
doc.text(line, 25, yPosition);
|
|
yPosition += 7;
|
|
}
|
|
yPosition += 8; // Extra space after hash
|
|
|
|
// Footer
|
|
const pageCount = doc.getNumberOfPages();
|
|
for (let i = 1; i <= pageCount; i++) {
|
|
doc.setPage(i);
|
|
doc.setFontSize(10);
|
|
doc.setFont('helvetica', 'italic');
|
|
doc.text(`Page ${i} sur ${pageCount}`, 105, 280, { align: 'center' });
|
|
doc.text(`Certificat Notarial - Généré le ${new Date().toLocaleString('fr-FR')}`, 105, 285, { align: 'center' });
|
|
}
|
|
|
|
return doc.output('blob');
|
|
} catch (error) {
|
|
console.error('Error generating notary certificate:', error);
|
|
throw new Error('Failed to generate notary certificate');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Download a notary certificate PDF
|
|
* @param certificateData - Data needed for the certificate
|
|
* @param filename - Optional filename for the download
|
|
*/
|
|
public async downloadNotaryCertificate(certificateData: CertificateData, filename?: string): Promise<void> {
|
|
try {
|
|
const pdfBlob = await this.generateNotaryCertificate(certificateData);
|
|
const defaultFilename = `notary_certificate_${certificateData.metadata.documentUid}_${new Date().toISOString().split('T')[0]}.pdf`;
|
|
saveAs(pdfBlob, filename || defaultFilename);
|
|
} catch (error) {
|
|
console.error('Error downloading notary certificate:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
}
|