feat: Add Digital Signature and Validate Signature tools

New Features:
- Digital Signature tool: Sign PDFs with X.509 certificates (PFX/PEM)
  - Visible and invisible signatures
  - All pages, first page, last page, or custom page selection
  - Dynamic signature height based on text content
  - Custom signature text, image, and styling options

- Validate Signature tool: Verify digital signatures in PDFs
  - Extract and parse PKCS#7 signatures
  - View signer and issuer certificate details
  - Check certificate validity and expiry
  - Optional custom X.509 certificate for trust verification
  - Full/partial coverage detection

Infrastructure:
- Added Cloudflare Worker CORS proxy for certificate chain fetching
- Updated README with new tools and proxy deployment instructions

Documentation:
- Added Digital Signature and Validate Signature to README tools table
- Added CORS proxy deployment guide for self-hosters
This commit is contained in:
abdullahalam123
2026-01-04 19:08:50 +05:30
parent 94504d4b75
commit 05110c7f6a
20 changed files with 1703 additions and 24 deletions

View File

@@ -596,15 +596,28 @@ async function processSignature(): Promise<void> {
const sigX = parseInt(getElement<HTMLInputElement>('sig-x')?.value ?? '25', 10);
const sigY = parseInt(getElement<HTMLInputElement>('sig-y')?.value ?? '700', 10);
const sigWidth = parseInt(getElement<HTMLInputElement>('sig-width')?.value ?? '150', 10);
const sigHeight = parseInt(getElement<HTMLInputElement>('sig-height')?.value ?? '50', 10);
const sigHeight = parseInt(getElement<HTMLInputElement>('sig-height')?.value ?? '70', 10);
const sigPageSelect = getElement<HTMLSelectElement>('sig-page');
let sigPage: number | string = 0;
let numPages = 1;
try {
const pdfDoc = await getPDFDocument({ data: state.pdfBytes.slice() }).promise;
numPages = pdfDoc.numPages;
} catch (error) {
console.error('Error getting PDF page count:', error);
}
if (sigPageSelect) {
if (sigPageSelect.value === 'last') {
sigPage = 'last';
sigPage = (numPages - 1).toString();
} else if (sigPageSelect.value === 'all') {
sigPage = 'all';
if (numPages === 1) {
sigPage = '0';
} else {
sigPage = `0-${numPages - 1}`;
}
} else if (sigPageSelect.value === 'custom') {
sigPage = parseInt(getElement<HTMLInputElement>('sig-custom-page')?.value ?? '1', 10) - 1;
} else {
@@ -623,12 +636,21 @@ async function processSignature(): Promise<void> {
sigText = `Digitally signed by ${certInfo.subject}\n${date}`;
}
let finalHeight = sigHeight;
if (sigText && !state.sigImageData) {
const lineCount = (sigText.match(/\n/g) || []).length + 1;
const lineHeightFactor = 1.4;
const padding = 16;
const calculatedHeight = Math.ceil(lineCount * sigTextSize * lineHeightFactor + padding);
finalHeight = Math.max(calculatedHeight, sigHeight);
}
visibleSignature = {
enabled: true,
x: sigX,
y: sigY,
width: sigWidth,
height: sigHeight,
height: finalHeight,
page: sigPage,
imageData: state.sigImageData ?? undefined,
imageType: state.sigImageType ?? undefined,
@@ -658,7 +680,16 @@ async function processSignature(): Promise<void> {
hideLoader();
console.error('Signing error:', error);
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
showAlert('Signing Failed', `Failed to sign PDF: ${errorMessage}`);
// Check if this is a CORS/network error from certificate chain fetching
if (errorMessage.includes('Failed to fetch') || errorMessage.includes('CORS') || errorMessage.includes('NetworkError')) {
showAlert(
'Signing Failed',
'Failed to fetch certificate chain. This may be due to network issues or the certificate proxy being unavailable. Please check your internet connection and try again. If the issue persists, contact support.'
);
} else {
showAlert('Signing Failed', `Failed to sign PDF: ${errorMessage}`);
}
}
}